diff --git a/go.mod b/go.mod index 967110c1..28ab5fc5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/cjfinnell/pgverify -go 1.25.0 +go 1.26.0 require ( github.com/google/uuid v1.6.0 @@ -8,9 +8,9 @@ require ( github.com/jackc/pgx/v5 v5.10.0 github.com/olekukonko/tablewriter v1.1.4 github.com/pkg/errors v0.9.1 - github.com/sirupsen/logrus v1.9.4 + github.com/sirupsen/logrus v1.10.1 github.com/spf13/cobra v1.10.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/testcontainers/testcontainers-go v0.44.0 github.com/testcontainers/testcontainers-go/modules/cockroachdb v0.44.0 github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0 @@ -18,60 +18,63 @@ require ( ) require ( - 4d63.com/gocheckcompilerdirectives v1.3.0 // indirect + 4d63.com/gocheckcompilerdirectives v1.4.0 // indirect 4d63.com/gochecknoglobals v0.2.2 // indirect + charm.land/lipgloss/v2 v2.0.6 // indirect codeberg.org/chavacava/garif v0.2.0 // indirect codeberg.org/polyfloyd/go-errorlint v1.9.0 // indirect dario.cat/mergo v1.0.2 // indirect dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect - dev.gaijin.team/go/golib v0.6.0 // indirect + dev.gaijin.team/go/exhaustruct/v5 v5.0.3 // indirect + dev.gaijin.team/go/golib v0.8.1 // indirect github.com/4meepo/tagalign v1.4.3 // indirect - github.com/Abirdcfly/dupword v0.1.7 // indirect + github.com/Abirdcfly/dupword v0.1.8 // indirect github.com/AdminBenni/iota-mixing v1.0.0 // indirect - github.com/AlwxSin/noinlineerr v1.0.5 // indirect - github.com/Antonboom/errname v1.1.1 // indirect - github.com/Antonboom/nilnil v1.1.1 // indirect + github.com/AlwxSin/noinlineerr v1.0.6 // indirect + github.com/Antonboom/errname v1.1.2 // indirect + github.com/Antonboom/nilnil v1.1.2 // indirect github.com/Antonboom/testifylint v1.6.4 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/BurntSushi/toml v1.6.0 // indirect + github.com/ClickHouse/clickhouse-go-linter v1.2.1 // indirect github.com/Djarvur/go-err113 v0.1.1 // indirect - github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Masterminds/semver/v3 v3.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/MirrexOne/unqueryvet v1.5.4 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect - github.com/alecthomas/chroma/v2 v2.23.1 // indirect + github.com/alecthomas/chroma/v2 v2.27.0 // indirect github.com/alecthomas/go-check-sumtype v0.3.1 // indirect github.com/alexkohler/nakedret/v2 v2.0.6 // indirect github.com/alexkohler/prealloc v1.1.0 // indirect github.com/alfatraining/structtag v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect github.com/alingse/nilnesserr v0.2.0 // indirect - github.com/ashanbrown/forbidigo/v2 v2.3.0 // indirect - github.com/ashanbrown/makezero/v2 v2.1.0 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/ashanbrown/forbidigo/v2 v2.3.1 // indirect + github.com/ashanbrown/makezero/v2 v2.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bitfield/gotestdox v0.2.2 // indirect github.com/bkielbasa/cyclop v1.2.3 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect github.com/bombsimon/wsl/v4 v4.7.0 // indirect - github.com/bombsimon/wsl/v5 v5.6.0 // indirect + github.com/bombsimon/wsl/v5 v5.9.0 // indirect github.com/breml/bidichk v0.3.3 // indirect github.com/breml/errchkjson v0.4.1 // indirect - github.com/butuzov/ireturn v0.4.0 // indirect - github.com/butuzov/mirror v1.3.0 // indirect + github.com/butuzov/ireturn v0.4.1 // indirect + github.com/butuzov/mirror v1.3.3 // indirect github.com/catenacyber/perfsprint v0.10.1 // indirect github.com/ccojocar/zxcvbn-go v1.0.4 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.11 // indirect - github.com/charmbracelet/colorprofile v0.3.1 // indirect - github.com/charmbracelet/lipgloss v1.1.0 // indirect - github.com/charmbracelet/x/ansi v0.10.1 // indirect - github.com/charmbracelet/x/cellbuf v0.0.13 // indirect - github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/charmbracelet/colorprofile v0.4.3 // indirect + github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886 // indirect + github.com/charmbracelet/x/ansi v0.11.8 // indirect + github.com/charmbracelet/x/term v0.2.2 // indirect + github.com/charmbracelet/x/termios v0.1.1 // indirect + github.com/charmbracelet/x/windows v0.2.2 // indirect github.com/ckaznocha/intrange v0.3.1 // indirect - github.com/clipperhouse/displaywidth v0.10.0 // indirect - github.com/clipperhouse/uax29/v2 v2.6.0 // indirect + github.com/clipperhouse/displaywidth v0.11.0 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect @@ -80,23 +83,22 @@ require ( github.com/curioswitch/go-reassign v0.3.0 // indirect github.com/daixiang0/gci v0.13.7 // indirect github.com/dave/dst v0.27.3 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/dlclark/regexp2/v2 v2.2.1 // indirect github.com/dnephin/pflag v1.0.7 // indirect github.com/docker/go-connections v0.7.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/ebitengine/purego v0.10.1 // indirect + github.com/ebitengine/purego v0.10.2 // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/color v1.19.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/felixge/httpsnoop v1.1.0 // indirect - github.com/firefart/nonamedreturns v1.0.6 // indirect + github.com/firefart/nonamedreturns v1.0.8 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/ghostiam/protogetter v0.3.20 // indirect - github.com/go-critic/go-critic v0.14.3 // indirect + github.com/ghostiam/protogetter v0.3.21 // indirect + github.com/go-critic/go-critic v0.14.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -113,14 +115,15 @@ require ( github.com/godoc-lint/godoc-lint v0.11.2 // indirect github.com/gofrs/flock v0.13.0 // indirect github.com/golangci/asciicheck v0.5.0 // indirect - github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect + github.com/golangci/dupl v0.0.0-20260401084720-c99c5cf5c202 // indirect github.com/golangci/go-printf-func-name v0.1.1 // indirect - github.com/golangci/gofmt v0.0.0-20250704145412-3e58ba0443c6 // indirect - github.com/golangci/golangci-lint/v2 v2.11.4 // indirect + github.com/golangci/gofmt v0.0.0-20260820135601-e84e05053792 // indirect + github.com/golangci/golangci-lint/v2 v2.13.1 // indirect github.com/golangci/golines v0.15.0 // indirect github.com/golangci/misspell v0.8.0 // indirect github.com/golangci/plugin-module-register v0.1.2 // indirect github.com/golangci/revgrep v0.8.0 // indirect + github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba // indirect github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect github.com/google/go-cmp v0.7.0 // indirect @@ -131,43 +134,42 @@ require ( github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect github.com/gostaticanalysis/nilerr v0.1.2 // indirect github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect - github.com/hashicorp/go-version v1.8.0 // indirect + github.com/hashicorp/go-version v1.9.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/jgautheron/goconst v1.8.2 // indirect - github.com/jingyugao/rowserrcheck v1.1.1 // indirect + github.com/jgautheron/goconst v1.11.0 // indirect github.com/jjti/go-spancheck v0.6.5 // indirect github.com/julz/importas v0.2.0 // indirect github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect - github.com/kisielk/errcheck v1.10.0 // indirect + github.com/kisielk/errcheck v1.20.0 // indirect github.com/kkHAIKE/contextcheck v1.1.6 // indirect github.com/klauspost/compress v1.18.6 // indirect github.com/kulti/thelper v0.7.1 // indirect github.com/kunwardeep/paralleltest v1.0.15 // indirect github.com/lasiar/canonicalheader v1.1.2 // indirect github.com/ldez/exptostd v0.4.5 // indirect - github.com/ldez/gomoddirectives v0.8.0 // indirect + github.com/ldez/gomoddirectives v0.9.0 // indirect github.com/ldez/grignotin v0.10.1 // indirect github.com/ldez/structtags v0.6.1 // indirect github.com/ldez/tagliatelle v0.7.2 // indirect github.com/ldez/usetesting v0.5.0 // indirect github.com/leonklingele/grouper v1.1.2 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/lucasb-eyer/go-colorful v1.4.1 // indirect github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e // indirect github.com/macabu/inamedparam v0.2.0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect - github.com/manuelarte/funcorder v0.5.0 // indirect + github.com/manuelarte/funcorder v0.6.0 // indirect github.com/maratori/testableexamples v1.0.1 // indirect github.com/maratori/testpackage v1.1.2 // indirect github.com/matoous/godox v1.1.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-colorable v0.1.15 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.19 // indirect + github.com/mattn/go-runewidth v0.0.24 // indirect github.com/mgechev/revive v1.15.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect @@ -180,19 +182,18 @@ require ( github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.2 // indirect github.com/moricho/tparallel v0.3.2 // indirect - github.com/muesli/termenv v0.16.0 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nakabonne/nestif v0.3.1 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.23.0 // indirect + github.com/nunnatsa/ginkgolinter v0.24.0 // indirect github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect github.com/olekukonko/errors v1.2.0 // indirect github.com/olekukonko/ll v0.1.6 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -203,22 +204,23 @@ require ( github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect - github.com/raeperd/recvcheck v0.2.0 // indirect + github.com/raeperd/recvcheck v0.3.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/rogpeppe/go-internal v1.16.0 // indirect github.com/ryancurrah/gomodguard v1.4.1 // indirect + github.com/ryancurrah/gomodguard/v2 v2.1.3 // indirect github.com/ryanrolds/sqlclosecheck v0.6.0 // indirect github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect - github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect - github.com/securego/gosec/v2 v2.24.8-0.20260309165252-619ce2117e08 // indirect - github.com/shirou/gopsutil/v4 v4.26.6 // indirect + github.com/securego/gosec/v2 v2.28.0 // indirect + github.com/shirou/gopsutil/v4 v4.26.7 // indirect github.com/sivchari/containedctx v1.0.3 // indirect github.com/sonatard/noctx v0.5.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect - github.com/sourcegraph/go-diff v0.7.0 // indirect + github.com/sourcegraph/go-diff v0.8.0 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.9.2 // indirect github.com/spf13/pflag v1.0.10 // indirect @@ -227,8 +229,8 @@ require ( github.com/stbenjam/no-sprintf-host-port v0.3.1 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tetafro/godot v1.5.4 // indirect - github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect + github.com/tetafro/godot v1.5.6 // indirect + github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 // indirect github.com/timonwong/loggercheck v0.11.0 // indirect github.com/tklauser/go-sysconf v0.4.0 // indirect github.com/tklauser/numcpus v0.12.0 // indirect @@ -237,7 +239,7 @@ require ( github.com/ultraware/funlen v0.2.0 // indirect github.com/ultraware/whitespace v0.2.0 // indirect github.com/uudashr/gocognit v1.2.1 // indirect - github.com/uudashr/iface v1.4.1 // indirect + github.com/uudashr/iface v1.5.0 // indirect github.com/xen0n/gosmopolitan v1.3.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yagipy/maintidx v1.0.0 // indirect @@ -246,30 +248,30 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/bosi/decorder v0.4.2 // indirect go-simpler.org/musttag v0.14.0 // indirect - go-simpler.org/sloglint v0.11.1 // indirect + go-simpler.org/sloglint v0.12.0 // indirect go.augendre.info/arangolint v0.4.0 // indirect - go.augendre.info/fatcontext v0.9.0 // indirect + go.augendre.info/fatcontext v0.10.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect go.opentelemetry.io/otel v1.44.0 // indirect go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/otel/trace v1.44.0 // indirect go.uber.org/zap v1.27.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.54.0 // indirect - golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect - golang.org/x/mod v0.37.0 // indirect + golang.org/x/exp/typeparams v0.0.0-20260811152304-ee035b5b010f // indirect + golang.org/x/mod v0.40.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/term v0.45.0 // indirect golang.org/x/text v0.40.0 // indirect - golang.org/x/tools v0.47.0 // indirect - google.golang.org/protobuf v1.36.8 // indirect + golang.org/x/tools v0.49.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/gotestsum v1.13.0 // indirect - honnef.co/go/tools v0.7.0 // indirect - mvdan.cc/gofumpt v0.9.2 // indirect - mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect + honnef.co/go/tools v0.8.0 // indirect + mvdan.cc/gofumpt v0.11.0 // indirect + mvdan.cc/unparam v0.0.0-20260818115549-3f964bcb5673 // indirect ) tool ( diff --git a/go.sum b/go.sum index 9fde9cde..ce833263 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,9 @@ -4d63.com/gocheckcompilerdirectives v1.3.0 h1:Ew5y5CtcAAQeTVKUVFrE7EwHMrTO6BggtEj8BZSjZ3A= -4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY= +4d63.com/gocheckcompilerdirectives v1.4.0 h1:ZLq62rbGWVmQhiZ8kuNVIT/M09xCSTdJz9K3xOdT/CY= +4d63.com/gocheckcompilerdirectives v1.4.0/go.mod h1:9ZOAiMOjqC/nRwci2fcUXVHUNLG/cH6r6rhUh+jTFtQ= 4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU= 4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= +charm.land/lipgloss/v2 v2.0.6 h1:EaGKeuA8FvF+v2BT5VmZd2LoYLaMZJXA5n34th8nCIQ= +charm.land/lipgloss/v2 v2.0.6/go.mod h1:ipDDJNSGa1hlwDtSfW1s2/xR8Vdhbut4PXh2zEKZd0Q= codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY= codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ= codeberg.org/polyfloyd/go-errorlint v1.9.0 h1:VkdEEmA1VBpH6ecQoMR4LdphVI3fA4RrCh2an7YmodI= @@ -10,32 +12,36 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKfwODoI1Y= dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI= -dev.gaijin.team/go/golib v0.6.0 h1:v6nnznFTs4bppib/NyU1PQxobwDHwCXXl15P7DV5Zgo= -dev.gaijin.team/go/golib v0.6.0/go.mod h1:uY1mShx8Z/aNHWDyAkZTkX+uCi5PdX7KsG1eDQa2AVE= +dev.gaijin.team/go/exhaustruct/v5 v5.0.3 h1:yOeA7DNjlT8y4yfmN6nWWYYggA13N523YAj9/TXbuTM= +dev.gaijin.team/go/exhaustruct/v5 v5.0.3/go.mod h1:KwtBsX8nHHH1YxhxkpiBq6bfsmw5WnazWpNvJPHgY9Y= +dev.gaijin.team/go/golib v0.8.1 h1:JYju4x9BSo+QD/AYeHULVDcvEhiFg8wOi6pT0IaZF5E= +dev.gaijin.team/go/golib v0.8.1/go.mod h1:c5fu7t1RSGMxSQgcUYO1sODbzsYnOCXJLmHeNG1Eb+0= github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8= github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c= -github.com/Abirdcfly/dupword v0.1.7 h1:2j8sInznrje4I0CMisSL6ipEBkeJUJAmK1/lfoNGWrQ= -github.com/Abirdcfly/dupword v0.1.7/go.mod h1:K0DkBeOebJ4VyOICFdppB23Q0YMOgVafM0zYW0n9lF4= +github.com/Abirdcfly/dupword v0.1.8 h1:SrhcUuGsROBuChFxHALRYzyyPODWn9zwghmzPvD9Cd8= +github.com/Abirdcfly/dupword v0.1.8/go.mod h1:XZrhVnI7YGpsTiWZANSQaBJ4QpL/Tq5vIEdKJJAs9WI= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdminBenni/iota-mixing v1.0.0 h1:Os6lpjG2dp/AE5fYBPAA1zfa2qMdCAWwPMCgpwKq7wo= github.com/AdminBenni/iota-mixing v1.0.0/go.mod h1:i4+tpAaB+qMVIV9OK3m4/DAynOd5bQFaOu+2AhtBCNY= -github.com/AlwxSin/noinlineerr v1.0.5 h1:RUjt63wk1AYWTXtVXbSqemlbVTb23JOSRiNsshj7TbY= -github.com/AlwxSin/noinlineerr v1.0.5/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc= -github.com/Antonboom/errname v1.1.1 h1:bllB7mlIbTVzO9jmSWVWLjxTEbGBVQ1Ff/ClQgtPw9Q= -github.com/Antonboom/errname v1.1.1/go.mod h1:gjhe24xoxXp0ScLtHzjiXp0Exi1RFLKJb0bVBtWKCWQ= -github.com/Antonboom/nilnil v1.1.1 h1:9Mdr6BYd8WHCDngQnNVV0b554xyisFioEKi30sksufQ= -github.com/Antonboom/nilnil v1.1.1/go.mod h1:yCyAmSw3doopbOWhJlVci+HuyNRuHJKIv6V2oYQa8II= +github.com/AlwxSin/noinlineerr v1.0.6 h1:KAvuxunTe9QxvqrFB7nZTdb/7Wzas4AvifslTnG0Ld8= +github.com/AlwxSin/noinlineerr v1.0.6/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc= +github.com/Antonboom/errname v1.1.2 h1:dxwONZJua3VB8Xh/VaCjqAcqF645sWWv7xj26zy7tdQ= +github.com/Antonboom/errname v1.1.2/go.mod h1:YeZIpgLMxT+SNkruGgYkLhzq/9vs3fsolTZegKaKDZI= +github.com/Antonboom/nilnil v1.1.2 h1:aNlFuJhaEseXe4fHO3xbjXlSeEiQVYa2lEkWD2s2hAY= +github.com/Antonboom/nilnil v1.1.2/go.mod h1:0ynwvphOLmAuMwTNDyBnDZmSwZoDpcFXmUHmzoHH2WA= github.com/Antonboom/testifylint v1.6.4 h1:gs9fUEy+egzxkEbq9P4cpcMB6/G0DYdMeiFS87UiqmQ= github.com/Antonboom/testifylint v1.6.4/go.mod h1:YO33FROXX2OoUfwjz8g+gUxQXio5i9qpVy7nXGbxDD4= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/ClickHouse/clickhouse-go-linter v1.2.1 h1:zGEKIyd5YL08ieWG/LOUmlau2DxbxPVOfAeo+4Jz3ck= +github.com/ClickHouse/clickhouse-go-linter v1.2.1/go.mod h1:pLorS7ffPTfuUV9M0SJgfHA/h/WQPQUk2FWG9x74cQ4= github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao4g= github.com/Djarvur/go-err113 v0.1.1/go.mod h1:IaWJdYFLg76t2ihfflPZnM1LIQszWOsFDh2hhhAVF6k= -github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= -github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= +github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/MirrexOne/unqueryvet v1.5.4 h1:38QOxShO7JmMWT+eCdDMbcUgGCOeJphVkzzRgyLJgsQ= @@ -44,8 +50,8 @@ github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsu github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY= -github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o= +github.com/alecthomas/chroma/v2 v2.27.0 h1:FodwmyOBgJULFYmDqibcp9pvfDLWdtPRh9v/r5BXYZs= +github.com/alecthomas/chroma/v2 v2.27.0/go.mod h1:NjJ3ciIgrqBNeIkWZ4e46nseoLDslxU1LmfCoL+wcY8= github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU= github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E= github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= @@ -60,12 +66,10 @@ github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQ github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w= github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= -github.com/ashanbrown/forbidigo/v2 v2.3.0 h1:OZZDOchCgsX5gvToVtEBoV2UWbFfI6RKQTir2UZzSxo= -github.com/ashanbrown/forbidigo/v2 v2.3.0/go.mod h1:5p6VmsG5/1xx3E785W9fouMxIOkvY2rRV9nMdWadd6c= -github.com/ashanbrown/makezero/v2 v2.1.0 h1:snuKYMbqosNokUKm+R6/+vOPs8yVAi46La7Ck6QYSaE= -github.com/ashanbrown/makezero/v2 v2.1.0/go.mod h1:aEGT/9q3S8DHeE57C88z2a6xydvgx8J5hgXIGWgo0MY= -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/ashanbrown/forbidigo/v2 v2.3.1 h1:KAZijvQ7zeIBKbhikT4jCm0TLYXC4u78bTiLh/8JROI= +github.com/ashanbrown/forbidigo/v2 v2.3.1/go.mod h1:2QDkLTzU6TV937eFROamXrW92M3paehdae4HCDCOZCM= +github.com/ashanbrown/makezero/v2 v2.2.1 h1:A7uU8dgB1PA9aelTxHMfHIQ8Qev8AB3JLxJUBUsejqM= +github.com/ashanbrown/makezero/v2 v2.2.1/go.mod h1:aEGT/9q3S8DHeE57C88z2a6xydvgx8J5hgXIGWgo0MY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bitfield/gotestdox v0.2.2 h1:x6RcPAbBbErKLnapz1QeAlf3ospg8efBsedU93CDsnE= @@ -76,16 +80,16 @@ github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ= github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg= -github.com/bombsimon/wsl/v5 v5.6.0 h1:4z+/sBqC5vUmSp1O0mS+czxwH9+LKXtCWtHH9rZGQL8= -github.com/bombsimon/wsl/v5 v5.6.0/go.mod h1:Uqt2EfrMj2NV8UGoN1f1Y3m0NpUVCsUdrNCdet+8LvU= +github.com/bombsimon/wsl/v5 v5.9.0 h1:WCrgZ7RQnZO5oEwbVTlYgBdU3wL294kR1BSWV8vTfsU= +github.com/bombsimon/wsl/v5 v5.9.0/go.mod h1:kjo4HiAV5FDkHC8/uzJq9mBffEEd6WT/nvN7DoMovDM= github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE= github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE= github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg= github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s= -github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E= -github.com/butuzov/ireturn v0.4.0/go.mod h1:ghI0FrCmap8pDWZwfPisFD1vEc56VKH4NpQUxDHta70= -github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc= -github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI= +github.com/butuzov/ireturn v0.4.1 h1:vWb3NO4t77iku/sjCQ/2pHTQeOmxEhjIriJqRLg1Y+I= +github.com/butuzov/ireturn v0.4.1/go.mod h1:q+DXKzTDV5guNuXLnIab9fKXizTn2miZHLhxH7V/GB4= +github.com/butuzov/mirror v1.3.3 h1:v0RsWBhfFc1RQqE/f3sHpSttKDtodFn0gFmtYyD4/hA= +github.com/butuzov/mirror v1.3.3/go.mod h1:h9BzzwYnTiHO0GzgvaTqIg7VSsOUhdIv51cHFFBmX1w= github.com/catenacyber/perfsprint v0.10.1 h1:u7Riei30bk46XsG8nknMhKLXG9BcXz3+3tl/WpKm0PQ= github.com/catenacyber/perfsprint v0.10.1/go.mod h1:DJTGsi/Zufpuus6XPGJyKOTMELe347o6akPvWG9Zcsc= github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc= @@ -96,22 +100,24 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.11 h1:g1/EX1eIiKS57NTWsYtHDZ/APfeXKhye1DidBcABctk= github.com/charithe/durationcheck v0.0.11/go.mod h1:x5iZaixRNl8ctbM+3B2RrPG5t856TxRyVQEnbIEM2X4= -github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40= -github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= -github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= -github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= -github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ= -github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= -github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= -github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= -github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= -github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q= +github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q= +github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886 h1:rdnVWKgJpTVXKuKuJyxDJ+NFJdUaUqGvyGy61OcvlbA= +github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886/go.mod h1:nAw0d9PhFp1qdzi2xhQU5YOu5sVpDIHWlaW2Uz/bCro= +github.com/charmbracelet/x/ansi v0.11.8 h1:JMFwp0CgDC2+jcOB162HH5k7I3FVbgFSMMYg7dSPBQQ= +github.com/charmbracelet/x/ansi v0.11.8/go.mod h1:ZNN+3mXny/516oTQPLMPIBeSINvNJJQ8uQXDgbeJxY0= +github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= +github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= +github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY= +github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= +github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM= +github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k= github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs= github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk= -github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g= -github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs= -github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos= -github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= +github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= @@ -134,22 +140,23 @@ github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEy github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= -github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8= +github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2/v2 v2.2.1 h1:mf4KkFUj0gJuarK8P+LgiS+Lit7m9N1yAwEfPbee7R0= +github.com/dlclark/regexp2/v2 v2.2.1/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU= github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk= github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY= -github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.2 h1:W809HbnvzAxgdm+aOvlSekrM16wGCdT/e76+9tS7gzE= +github.com/ebitengine/purego v0.10.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= @@ -158,18 +165,18 @@ github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4 github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc= github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= -github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E= -github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo= +github.com/firefart/nonamedreturns v1.0.8 h1:iB32Dl17zJl1zlVEj/WlUWgx0HiRyQ85OUw1WHa4/II= +github.com/firefart/nonamedreturns v1.0.8/go.mod h1:vxFNvm5AfP/8rgAKFzYmnqx0yp1HjrYsErZ9pHPTznA= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/ghostiam/protogetter v0.3.20 h1:oW7OPFit2FxZOpmMRPP9FffU4uUpfeE/rEdE1f+MzD0= -github.com/ghostiam/protogetter v0.3.20/go.mod h1:FjIu5Yfs6FT391m+Fjp3fbAYJ6rkL/J6ySpZBfnODuI= -github.com/go-critic/go-critic v0.14.3 h1:5R1qH2iFeo4I/RJU8vTezdqs08Egi4u5p6vOESA0pog= -github.com/go-critic/go-critic v0.14.3/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ= +github.com/ghostiam/protogetter v0.3.21 h1:EeWTGvL/Eyosp653hiWb6Byx4b69iJC4/E+za7vQHoI= +github.com/ghostiam/protogetter v0.3.21/go.mod h1:iAKSpyoHwYzay+OpjoWgwzRtPFthEfuUvmlomTThck0= +github.com/go-critic/go-critic v0.14.4 h1:dSX4C3pWSeuMVxvQh6yG8U0ReSf3YOmKi4nwX5q7n/8= +github.com/go-critic/go-critic v0.14.4/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -178,8 +185,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= +github.com/go-quicktest/qt v1.102.0 h1:HSQxCeh5YZH3EL3W39ixjtyaEhcWSXQHtHnMBzSs474= +github.com/go-quicktest/qt v1.102.0/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= @@ -213,14 +220,14 @@ github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/golangci/asciicheck v0.5.0 h1:jczN/BorERZwK8oiFBOGvlGPknhvq0bjnysTj4nUfo0= github.com/golangci/asciicheck v0.5.0/go.mod h1:5RMNAInbNFw2krqN6ibBxN/zfRFa9S6tA1nPdM0l8qQ= -github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 h1:WUvBfQL6EW/40l6OmeSBYQJNSif4O11+bmWEz+C7FYw= -github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E= +github.com/golangci/dupl v0.0.0-20260401084720-c99c5cf5c202 h1:CbTB8KpqnViI6lIXxp03Oclc4VFHi3K4BWC1TacsZ+A= +github.com/golangci/dupl v0.0.0-20260401084720-c99c5cf5c202/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E= github.com/golangci/go-printf-func-name v0.1.1 h1:hIYTFJqAGp1iwoIfsNTpoq1xZAarogrvjO9AfiW3B4U= github.com/golangci/go-printf-func-name v0.1.1/go.mod h1:Es64MpWEZbh0UBtTAICOZiB+miW53w/K9Or/4QogJss= -github.com/golangci/gofmt v0.0.0-20250704145412-3e58ba0443c6 h1:jlKy3uQkETB3zMBK8utduvojT+If2nDAM1pWpEzXjaY= -github.com/golangci/gofmt v0.0.0-20250704145412-3e58ba0443c6/go.mod h1:OyaRySOXorMn8zJqFku8YsKptIhPkANyKKTMC+rqMCs= -github.com/golangci/golangci-lint/v2 v2.11.4 h1:GK+UlZBN5y7rh2PBnHA93XLSX6RaF7uhzJQ3JwU1wuA= -github.com/golangci/golangci-lint/v2 v2.11.4/go.mod h1:ODQDCASMA3VqfZYIbbQLpTRTzV7O/vjmIRF6u8NyFwI= +github.com/golangci/gofmt v0.0.0-20260820135601-e84e05053792 h1:WL8YKrt3UbOBqSRU7GpP5BTtQTMWtVtj+mfPijgZeIg= +github.com/golangci/gofmt v0.0.0-20260820135601-e84e05053792/go.mod h1:te5hX0dW4C5r6YbXs+6ysNr8Q5UTmdIqGbb+mlFiYmA= +github.com/golangci/golangci-lint/v2 v2.13.1 h1:RuM4OcluM4xFQcGuRE6R7jA33pqxK/W1EsBxpugdZjg= +github.com/golangci/golangci-lint/v2 v2.13.1/go.mod h1:HwX7mDzqHbcSxlhrTygjX1GJbAfQ3sJAqOx41qQlhDE= github.com/golangci/golines v0.15.0 h1:Qnph25g8Y1c5fdo1X7GaRDGgnMHgnxh4Gk4VfPTtRx0= github.com/golangci/golines v0.15.0/go.mod h1:AZjXd23tbHMpowhtnGlj9KCNsysj72aeZVVHnVcZx10= github.com/golangci/misspell v0.8.0 h1:qvxQhiE2/5z+BVRo1kwYA8yGz+lOlu5Jfvtx2b04Jbg= @@ -229,6 +236,8 @@ github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3H github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s= github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= +github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba h1:lqtcnSMDuuJdu/LrKWi5RJzpSNLOJXYe/nzQutTI5kg= +github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba/go.mod h1:sCBNcpRmhJCtbFGz49+IM3ETTFf7QdJ30AeYCd43NKk= github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM= github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s= github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM= @@ -238,8 +247,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= -github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= +github.com/google/pprof v0.0.0-20260709232956-b9395ee17fa0 h1:du0WGc8xSKq/++e0cglxhS/mXVqsR7+c7jLEi5Vqduw= +github.com/google/pprof v0.0.0-20260709232956-b9395ee17fa0/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -263,8 +272,8 @@ github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1T github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= -github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA= +github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -281,18 +290,16 @@ github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0= github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4= -github.com/jgautheron/goconst v1.8.2/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako= -github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= -github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= +github.com/jgautheron/goconst v1.11.0 h1:KgN90z5qXt5f0Uzf3cWXev3hfMMFUyNeKdpkSBRvLDk= +github.com/jgautheron/goconst v1.11.0/go.mod h1:0p+wv1lFOiUr0IlNNT1nrm6+8DB8u2sU6KHGzFRXHDc= github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8= github.com/jjti/go-spancheck v0.6.5/go.mod h1:aEogkeatBrbYsyW6y5TgDfihCulDYciL1B7rG2vSsrU= github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY= github.com/karamaru-alpha/copyloopvar v1.2.2 h1:yfNQvP9YaGQR7VaWLYcfZUlRP2eo2vhExWKxD/fP6q0= github.com/karamaru-alpha/copyloopvar v1.2.2/go.mod h1:oY4rGZqZ879JkJMtX3RRkcXRkmUvH0x35ykgaKgsgJY= -github.com/kisielk/errcheck v1.10.0 h1:Lvs/YAHP24YKg08LA8oDw2z9fJVme090RAXd90S+rrw= -github.com/kisielk/errcheck v1.10.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8= +github.com/kisielk/errcheck v1.20.0 h1:9rwHBNKzd4wkDWcROy3DvFGNqEPlkxBg305rvk7HabI= +github.com/kisielk/errcheck v1.20.0/go.mod h1:O+f80MKNwX8Oor2jwgpeQ9An7uJm+hRSgT+h22knRJU= github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE= github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg= github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= @@ -309,8 +316,8 @@ github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0 github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= github.com/ldez/exptostd v0.4.5 h1:kv2ZGUVI6VwRfp/+bcQ6Nbx0ghFWcGIKInkG/oFn1aQ= github.com/ldez/exptostd v0.4.5/go.mod h1:QRjHRMXJrCTIm9WxVNH6VW7oN7KrGSht69bIRwvdFsM= -github.com/ldez/gomoddirectives v0.8.0 h1:JqIuTtgvFC2RdH1s357vrE23WJF2cpDCPFgA/TWDGpk= -github.com/ldez/gomoddirectives v0.8.0/go.mod h1:jutzamvZR4XYJLr0d5Honycp4Gy6GEg2mS9+2YX3F1Q= +github.com/ldez/gomoddirectives v0.9.0 h1:2YV/EX7nVlWL4jySusYTzBKHuE3D2fgcRsQuMa3yIoo= +github.com/ldez/gomoddirectives v0.9.0/go.mod h1:DdZzfm9MdXCjn2/UGYXCFfo+tzrp2Ib4iD2Q0kIJkwE= github.com/ldez/grignotin v0.10.1 h1:keYi9rYsgbvqAZGI1liek5c+jv9UUjbvdj3Tbn5fn4o= github.com/ldez/grignotin v0.10.1/go.mod h1:UlDbXFCARrXbWGNGP3S5vsysNXAPhnSuBufpTEbwOas= github.com/ldez/structtags v0.6.1 h1:bUooFLbXx41tW8SvkfwfFkkjPYvFFs59AAMgVg6DUBk= @@ -321,10 +328,10 @@ github.com/ldez/usetesting v0.5.0 h1:3/QtzZObBKLy1F4F8jLuKJiKBjjVFi1IavpoWbmqLwc github.com/ldez/usetesting v0.5.0/go.mod h1:Spnb4Qppf8JTuRgblLrEWb7IE6rDmUpGvxY3iRrzvDQ= github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= -github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs= -github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= +github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/lucasb-eyer/go-colorful v1.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss= +github.com/lucasb-eyer/go-colorful v1.4.1/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE= @@ -333,8 +340,8 @@ github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8S github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/manuelarte/embeddedstructfieldcheck v0.4.0 h1:3mAIyaGRtjK6EO9E73JlXLtiy7ha80b2ZVGyacxgfww= github.com/manuelarte/embeddedstructfieldcheck v0.4.0/go.mod h1:z8dFSyXqp+fC6NLDSljRJeNQJJDWnY7RoWFzV3PC6UM= -github.com/manuelarte/funcorder v0.5.0 h1:llMuHXXbg7tD0i/LNw8vGnkDTHFpTnWqKPI85Rknc+8= -github.com/manuelarte/funcorder v0.5.0/go.mod h1:Yt3CiUQthSBMBxjShjdXMexmzpP8YGvGLjrxJNkO2hA= +github.com/manuelarte/funcorder v0.6.0 h1:0hBngc4fa1IgNiI65A7sFGkMvoMCc878RjqB5V7rWP0= +github.com/manuelarte/funcorder v0.6.0/go.mod h1:id3NDhXdQBmeqXH7eVC6Z89xS6JxvZ8kF9xUxpArU/g= github.com/maratori/testableexamples v1.0.1 h1:HfOQXs+XgfeRBJ+Wz0XfH+FHnoY9TVqL6Fcevpzy4q8= github.com/maratori/testableexamples v1.0.1/go.mod h1:XE2F/nQs7B9N08JgyRmdGjYVGqxWwClLPCGSQhXQSrQ= github.com/maratori/testpackage v1.1.2 h1:ffDSh+AgqluCLMXhM19f/cpvQAKygKAJXFl9aUjmbqs= @@ -343,12 +350,12 @@ github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4= github.com/matoous/godox v1.1.0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= +github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= -github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= +github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o= github.com/mgechev/revive v1.15.0 h1:vJ0HzSBzfNyPbHKolgiFjHxLek9KUijhqh42yGoqZ8Q= @@ -375,8 +382,8 @@ github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= -github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= @@ -385,8 +392,8 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= 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.23.0 h1:x3o4DGYOWbBMP/VdNQKgSj+25aJKx2Pe6lHr8gBcgf8= -github.com/nunnatsa/ginkgolinter v0.23.0/go.mod h1:9qN1+0akwXEccwV1CAcCDfcoBlWXHB+ML9884pL4SZ4= +github.com/nunnatsa/ginkgolinter v0.24.0 h1:Mp0EagluLFP98JatP6nqp/gGEoljNG97uf9AcxcBVy8= +github.com/nunnatsa/ginkgolinter v0.24.0/go.mod h1:2ZMRuzX6+3XXyY6UZOwb6n+MCocVGbkIsDBC4vuWz5c= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo= @@ -395,10 +402,10 @@ github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA= github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88= github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I= github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY= -github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI= -github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= -github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= -github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= +github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E= +github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= +github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -410,11 +417,10 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= +github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= @@ -436,45 +442,45 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= -github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI= -github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU= +github.com/raeperd/recvcheck v0.3.0 h1:PM+XYvyxIj3bo+kobJfFTdTuU3Lmfu96mKDbyHDbRt8= +github.com/raeperd/recvcheck v0.3.0/go.mod h1:PZNwG+HztFYMH2ZPq0Hu3QgkV2yiA6VrtNz9c1fXWJo= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g= +github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryancurrah/gomodguard v1.4.1 h1:eWC8eUMNZ/wM/PWuZBv7JxxqT5fiIKSIyTvjb7Elr+g= github.com/ryancurrah/gomodguard v1.4.1/go.mod h1:qnMJwV1hX9m+YJseXEBhd2s90+1Xn6x9dLz11ualI1I= +github.com/ryancurrah/gomodguard/v2 v2.1.3 h1:E7sz3PJwE9Ba1reVxSpF6XLCPJZ74Kfw/LabTNM4GIA= +github.com/ryancurrah/gomodguard/v2 v2.1.3/go.mod h1:CQicdLGatWMxLX53JzoBjYlsNZhHbmLv2AVa0s2aivU= github.com/ryanrolds/sqlclosecheck v0.6.0 h1:pEyL9okISdg1F1SEpJNlrEotkTGerv5BMk7U4AG0eVg= github.com/ryanrolds/sqlclosecheck v0.6.0/go.mod h1:xyX16hsDaCMXHrMJ3JMzGf5OpDfHTOTTQrT7HOFUmeU= github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ= github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8= -github.com/securego/gosec/v2 v2.24.8-0.20260309165252-619ce2117e08 h1:AoLtJX4WUtZkhhUUMFy3GgecAALp/Mb4S1iyQOA2s0U= -github.com/securego/gosec/v2 v2.24.8-0.20260309165252-619ce2117e08/go.mod h1:+XLCJiRE95ga77XInNELh2M6zQP+PdqiT9Zpm0D9Wpk= +github.com/securego/gosec/v2 v2.28.0 h1:ZsSdiDb0AtTpLFVol5z91gbMei9ZiLEPG/pZjZujp7c= +github.com/securego/gosec/v2 v2.28.0/go.mod h1:lb4/9AHe+lJy/kjWmWRWWsEipvbwGKuxf+tY1Pmjdnk= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs= -github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= -github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/shirou/gopsutil/v4 v4.26.7 h1:IXzpHz/dkMRYAhKkOXr1HB6SuzWU3eoyyeWe7g3bNZc= +github.com/shirou/gopsutil/v4 v4.26.7/go.mod h1:5O9FjBiXoTDFatIWjZZosqj4pV0DRtLx598xGbBehzM= +github.com/sirupsen/logrus v1.10.1 h1:xi4336Zh11WpU14fXR6I67V3yaTPQYwRx2WEtHbRg4Q= +github.com/sirupsen/logrus v1.10.1/go.mod h1:vsQHnG7xzNsxk3NrwboUiWPnIC3dmbjcGPykD7+tiHk= 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/sonatard/noctx v0.5.1 h1:wklWg9c9ZYugOAk7qG4yP4PBrlQsmSLPTvW1K4PRQMs= github.com/sonatard/noctx v0.5.1/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= -github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= +github.com/sourcegraph/go-diff v0.8.0 h1:ipIyu4cTsLbIrln4l0qtHA3r0a7gyK4ntKjtQytHhvY= +github.com/sourcegraph/go-diff v0.8.0/go.mod h1:hWlcO7Al+UZStZAP8rBumHpCK5ZHQ5BXsMls8p4+F5E= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= @@ -497,8 +503,8 @@ github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+Q github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= @@ -511,10 +517,10 @@ github.com/testcontainers/testcontainers-go/modules/cockroachdb v0.44.0 h1:ghehz github.com/testcontainers/testcontainers-go/modules/cockroachdb v0.44.0/go.mod h1:r0sgLxlpK7wGO95cbdRf6N6pGvfoaJFjz4m5q/TYuqI= github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0 h1:8fdv/9y3JMxjQ+ULAcOG8RtgeNu5t9XF9LolSXDuTwM= github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0/go.mod h1:CFr2LncGYokw+OKjXcr8ARCKG1SaC2UEnGxFBovE86g= -github.com/tetafro/godot v1.5.4 h1:u1ww+gqpRLiIA16yF2PV1CV1n/X3zhyezbNXC3E14Sg= -github.com/tetafro/godot v1.5.4/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU= -github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk= -github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460= +github.com/tetafro/godot v1.5.6 h1:IEkrFCwXaYHlOn4mGzGS3F3dkP6m9t0jpwqBFPIkKiA= +github.com/tetafro/godot v1.5.6/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU= +github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 h1:SiHe5XLTn9sFWJ5pBwJ5FN/4j34q9ZlOAD//kMoMYp0= +github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4/go.mod h1:sDHLK7rb/59v/ZxZ7KtymgcoxuUMxjXq8gtu9VMOK8M= github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M= github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= @@ -531,8 +537,8 @@ github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSW github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= github.com/uudashr/gocognit v1.2.1 h1:CSJynt5txTnORn/DkhiB4mZjwPuifyASC8/6Q0I/QS4= github.com/uudashr/gocognit v1.2.1/go.mod h1:acaubQc6xYlXFEMb9nWX2dYBzJ/bIjEkc1zzvyIZg5Q= -github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU= -github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg= +github.com/uudashr/iface v1.5.0 h1:PgdMt4uAettGG8K/Kbamc4B9FABgUgnS3TLbl6fnjEk= +github.com/uudashr/iface v1.5.0/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg= github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM= github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= @@ -546,7 +552,6 @@ github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= @@ -557,12 +562,12 @@ go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= go-simpler.org/musttag v0.14.0 h1:XGySZATqQYSEV3/YTy+iX+aofbZZllJaqwFWs+RTtSo= go-simpler.org/musttag v0.14.0/go.mod h1:uP8EymctQjJ4Z1kUnjX0u2l60WfUdQxCwSNKzE1JEOE= -go-simpler.org/sloglint v0.11.1 h1:xRbPepLT/MHPTCA6TS/wNfZrDzkGvCCqUv4Bdwc3H7s= -go-simpler.org/sloglint v0.11.1/go.mod h1:2PowwiCOK8mjiF+0KGifVOT8ZsCNiFzvfyJeJOIt8MQ= +go-simpler.org/sloglint v0.12.0 h1:UzWDlLWNE5FLqsvyq3tWYHuQMbqrervOhT8qPl4Mmw4= +go-simpler.org/sloglint v0.12.0/go.mod h1:jBjjC2bm8rYrs88oTRlFX497kWjJsyZWYoNaXkGRI6I= go.augendre.info/arangolint v0.4.0 h1:xSCZjRoS93nXazBSg5d0OGCi9APPLNMmmLrC995tR50= go.augendre.info/arangolint v0.4.0/go.mod h1:l+f/b4plABuFISuKnTGD4RioXiCCgghv2xqst/xOvAA= -go.augendre.info/fatcontext v0.9.0 h1:Gt5jGD4Zcj8CDMVzjOJITlSb9cEch54hjRRlN3qDojE= -go.augendre.info/fatcontext v0.9.0/go.mod h1:L94brOAT1OOUNue6ph/2HnwxoNlds9aXDF2FcUntbNw= +go.augendre.info/fatcontext v0.10.0 h1:HhFopmivh8U1+AU7f0kuwUeg2eiIns7YsGQOMHwSJ90= +go.augendre.info/fatcontext v0.10.0/go.mod h1:pqpGvA9GlrXy+aXkp8L2dKz12Zp4g2FhzcAtwToU+2w= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo= @@ -583,48 +588,40 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= 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-20260209203927-2842357ff358 h1:qWFG1Dj7TBjOjOvhEOkmyGPVoquqUKnIU0lEVLp8xyk= -golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms= +golang.org/x/exp/typeparams v0.0.0-20260811152304-ee035b5b010f h1:+lI8cDJ4uceLipg2f1ODay7fEuLkk0BIHXd6PB8icxo= +golang.org/x/exp/typeparams v0.0.0-20260811152304-ee035b5b010f/go.mod h1:PqrXSW65cXDZH0k4IeUbhmg/bcAZDbzNz3byBpKCsXo= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= -golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs= +golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= -golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -632,8 +629,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -644,9 +639,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -656,17 +649,11 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -675,8 +662,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -685,14 +670,11 @@ golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWc golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= -golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= @@ -701,8 +683,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -714,11 +696,11 @@ gotest.tools/gotestsum v1.13.0 h1:+Lh454O9mu9AMG1APV4o0y7oDYKyik/3kBOiCqiEpRo= gotest.tools/gotestsum v1.13.0/go.mod h1:7f0NS5hFb0dWr4NtcsAsF0y1kzjEFfAil0HiBQJE03Q= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU= -honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc= -mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4= -mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s= -mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 h1:ssMzja7PDPJV8FStj7hq9IKiuiKhgz9ErWw+m68e7DI= -mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15/go.mod h1:4M5MMXl2kW6fivUT6yRGpLLPNfuGtU2Z0cPvFquGDYU= +honnef.co/go/tools v0.8.0 h1:UacpzPr7D6i5BAjTkA7sNVcx4kIbhAZcQ4zYtKiXx68= +honnef.co/go/tools v0.8.0/go.mod h1:XA+OnlRA9EDh/ukGvXMNSZNKGwFQJ+5dER0ioUkOxks= +mvdan.cc/gofumpt v0.11.0 h1:0H01XB95PnN2QgCSR9ELdZyTlJqNZ7181B0BTMh5VZc= +mvdan.cc/gofumpt v0.11.0/go.mod h1:BeT5wCsOJt6J9zT2MZIOGszjUHzFkn1/l9g6xAzqsXo= +mvdan.cc/unparam v0.0.0-20260818115549-3f964bcb5673 h1:dEE6li4OPIE54oojY2qaayFS1fSp17G14si0gXRxl0U= +mvdan.cc/unparam v0.0.0-20260818115549-3f964bcb5673/go.mod h1:62roFV3D3nYOWIXv3PfGO4UYEKAotz2WgLywT87ONd8= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/vendor/4d63.com/gocheckcompilerdirectives/checkcompilerdirectives/checkcompilerdirectives.go b/vendor/4d63.com/gocheckcompilerdirectives/checkcompilerdirectives/checkcompilerdirectives.go index e719155d..5a0e3574 100644 --- a/vendor/4d63.com/gocheckcompilerdirectives/checkcompilerdirectives/checkcompilerdirectives.go +++ b/vendor/4d63.com/gocheckcompilerdirectives/checkcompilerdirectives/checkcompilerdirectives.go @@ -86,6 +86,7 @@ var known = []string{ "cgo_unsafe_args", "debug", "embed", + "fix", "generate", "linkname", "nocheckptr", diff --git a/vendor/charm.land/lipgloss/v2/.editorconfig b/vendor/charm.land/lipgloss/v2/.editorconfig new file mode 100644 index 00000000..5de2df8c --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/.editorconfig @@ -0,0 +1,18 @@ +# https://editorconfig.org/ + +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.go] +indent_style = tab +indent_size = 8 + +[*.golden] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/vendor/charm.land/lipgloss/v2/.gitattributes b/vendor/charm.land/lipgloss/v2/.gitattributes new file mode 100644 index 00000000..d5273520 --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/.gitattributes @@ -0,0 +1 @@ +*.golden linguist-generated=true -text diff --git a/vendor/github.com/charmbracelet/lipgloss/.gitignore b/vendor/charm.land/lipgloss/v2/.gitignore similarity index 53% rename from vendor/github.com/charmbracelet/lipgloss/.gitignore rename to vendor/charm.land/lipgloss/v2/.gitignore index db482015..3b478f57 100644 --- a/vendor/github.com/charmbracelet/lipgloss/.gitignore +++ b/vendor/charm.land/lipgloss/v2/.gitignore @@ -1,2 +1,3 @@ ssh_example_ed25519* -dist/ +/tmp +**/.crush/** diff --git a/vendor/github.com/charmbracelet/lipgloss/.golangci.yml b/vendor/charm.land/lipgloss/v2/.golangci.yml similarity index 57% rename from vendor/github.com/charmbracelet/lipgloss/.golangci.yml rename to vendor/charm.land/lipgloss/v2/.golangci.yml index 90c5c08b..f2e6be06 100644 --- a/vendor/github.com/charmbracelet/lipgloss/.golangci.yml +++ b/vendor/charm.land/lipgloss/v2/.golangci.yml @@ -1,26 +1,12 @@ +version: "2" run: tests: false - -issues: - include: - - EXC0001 - - EXC0005 - - EXC0011 - - EXC0012 - - EXC0013 - - max-issues-per-linter: 0 - max-same-issues: 0 - linters: enable: - bodyclose - exhaustive - goconst - godot - - godox - - gofumpt - - goimports - gomoddirectives - goprintffuncname - gosec @@ -39,3 +25,26 @@ linters: - unparam - whitespace - wrapcheck + exclusions: + rules: + - text: '(slog|log)\.\w+' + linters: + - noctx + - text: "naming" + linters: + - revive + generated: lax + presets: + - common-false-positives + settings: + exhaustive: + default-signifies-exhaustive: true +issues: + max-issues-per-linter: 0 + max-same-issues: 0 +formatters: + enable: + - gofumpt + - goimports + exclusions: + generated: lax diff --git a/vendor/charm.land/lipgloss/v2/.goreleaser.yml b/vendor/charm.land/lipgloss/v2/.goreleaser.yml new file mode 100644 index 00000000..c61970e0 --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/.goreleaser.yml @@ -0,0 +1,5 @@ +includes: + - from_url: + url: charmbracelet/meta/main/goreleaser-lib.yaml +# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json + diff --git a/vendor/charm.land/lipgloss/v2/LICENSE b/vendor/charm.land/lipgloss/v2/LICENSE new file mode 100644 index 00000000..9f60dc1f --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-2026 Charmbracelet, Inc. + +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/charmbracelet/lipgloss/README.md b/vendor/charm.land/lipgloss/v2/README.md similarity index 61% rename from vendor/github.com/charmbracelet/lipgloss/README.md rename to vendor/charm.land/lipgloss/v2/README.md index cee2371c..4fda6400 100644 --- a/vendor/github.com/charmbracelet/lipgloss/README.md +++ b/vendor/charm.land/lipgloss/v2/README.md @@ -1,23 +1,21 @@ # Lip Gloss -

- Lip Gloss title treatment
+

+
Latest Release - GoDoc + GoDoc Build Status - phorm.ai

-Style definitions for nice terminal layouts. Built with TUIs in mind. +

Style definitions for nice terminal layouts. Built with TUIs in mind.

-![Lip Gloss example](https://github.com/user-attachments/assets/7950b1c1-e0e3-427e-8e7d-6f7f6ad17ca7) +![Lip Gloss example](https://github.com/user-attachments/assets/92560e60-d70e-4ce0-b39e-a60bb933356b) Lip Gloss takes an expressive, declarative approach to terminal rendering. Users familiar with CSS will feel at home with Lip Gloss. ```go - -import "github.com/charmbracelet/lipgloss" +import "charm.land/lipgloss/v2" var style = lipgloss.NewStyle(). Bold(true). @@ -27,9 +25,20 @@ var style = lipgloss.NewStyle(). PaddingLeft(4). Width(22) -fmt.Println(style.Render("Hello, kitty")) +lipgloss.Println(style.Render("Hello, kitty")) +``` + +## Installation + +```bash +go get charm.land/lipgloss/v2 ``` +> [!TIP] +> +> Upgrading from v1? Check out the [upgrade guide](./UPGRADE_GUIDE_V2.md), or +> point your LLM at it and let it go to town. + ## Colors Lip Gloss supports the following color profiles: @@ -60,45 +69,58 @@ lipgloss.Color("#3C3C3C") // a dark gray ...as well as a 1-bit ASCII profile, which is black and white only. -The terminal's color profile will be automatically detected, and colors outside -the gamut of the current palette will be automatically coerced to their closest -available value. - -### Adaptive Colors - -You can also specify color options for light and dark backgrounds: +There are also named constants for the 16 standard ANSI colors: ```go -lipgloss.AdaptiveColor{Light: "236", Dark: "248"} +lipgloss.Black +lipgloss.Red +lipgloss.Green +lipgloss.Yellow +lipgloss.Blue +lipgloss.Magenta +lipgloss.Cyan +lipgloss.White +lipgloss.BrightBlack +lipgloss.BrightRed +lipgloss.BrightGreen +lipgloss.BrightYellow +lipgloss.BrightBlue +lipgloss.BrightMagenta +lipgloss.BrightCyan +lipgloss.BrightWhite ``` -The terminal's background color will automatically be detected and the -appropriate color will be chosen at runtime. +### Automatically Downsampling Colors -### Complete Colors +Some users don't have Truecolor terminals. Other times, output might not +support color at all (for example, in logs). Lip Gloss was designed to handle +this gracefully by automatically downsampling colors to the best available +profile. -CompleteColor specifies exact values for True Color, ANSI256, and ANSI color -profiles. +If you're using Lip Gloss with Bubble Tea, there’s nothing to do. If you're +using Lip Gloss standalone, just use `lipgloss.Println` or `lipgloss.Sprint` +(and their variants). -```go -lipgloss.CompleteColor{TrueColor: "#0000FF", ANSI256: "86", ANSI: "5"} -``` +For more, see [advanced color usage](#advanced-color-usage). -Automatic color degradation will not be performed in this case and it will be -based on the color specified. +### Color Utilities -### Complete Adaptive Colors - -You can use `CompleteColor` with `AdaptiveColor` to specify the exact values for -light and dark backgrounds without automatic color degradation. +Lip Gloss ships with a handful of handy tools for working with colors: ```go -lipgloss.CompleteAdaptiveColor{ - Light: CompleteColor{TrueColor: "#d7ffae", ANSI256: "193", ANSI: "11"}, - Dark: CompleteColor{TrueColor: "#d75fee", ANSI256: "163", ANSI: "5"}, -} +c := lipgloss.Color("#EB4268") // Sriracha sauce color +dark := lipgloss.Darken(c, 0.5) // dark Sriracha sauce +light := lipgloss.Lighten(c, 0.35) // light Sriracha sauce +green := lipgloss.Complementary(c) // greenish Sriracha sauce +withAlpha := lipgloss.Alpha(c, 0.2) // watered down Sriracha sauce ``` +### Advanced Color Tooling + +Lip Gloss also supports color blending, automatically choosing light or dark +variants of colors at runtime, and a lot more. For details, see [Advanced Color +Usage](#advanced-color-usage) and [the docs][docs]. + ## Inline Formatting Lip Gloss supports the usual ANSI text formatting options: @@ -114,6 +136,34 @@ var style = lipgloss.NewStyle(). Reverse(true) ``` +### Underline Styles + +Beyond simple on/off, underlines support multiple styles and custom colors: + +```go +s := lipgloss.NewStyle(). + UnderlineStyle(lipgloss.UnderlineCurly). + UnderlineColor(lipgloss.Color("#FF0000")) +``` + +Available styles: `UnderlineNone`, `UnderlineSingle`, `UnderlineDouble`, +`UnderlineCurly`, `UnderlineDotted`, `UnderlineDashed`. + +### Hyperlinks + +Styles can render clickable hyperlinks in supporting terminals: + +```go +s := lipgloss.NewStyle(). + Foreground(lipgloss.Color("#7B2FBE")). + Hyperlink("https://charm.land") + +lipgloss.Println(s.Render("Visit Charm")) +``` + +In unsupported terminals this will degrade gracefully and hyperlinks will +simply not render. + ## Block-Level Formatting Lip Gloss also supports rules for block-level formatting: @@ -152,6 +202,16 @@ lipgloss.NewStyle().Padding(1, 4, 2) lipgloss.NewStyle().Margin(2, 4, 3, 1) ``` +You can also customize the characters used for padding and margin fill: + +```go +s := lipgloss.NewStyle(). + Padding(1, 2). + PaddingChar('·'). + Margin(1, 2). + MarginChar('░') +``` + ## Aligning Text You can align paragraphs of text to the left, right, or center. @@ -221,7 +281,15 @@ lipgloss.NewStyle(). Border(lipgloss.DoubleBorder(), true, false, false, true) ``` -For more on borders see [the docs][docs]. +You can also pass multiple colors to a border for a gradient effect: + +```go +s := lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForegroundBlend(lipgloss.Color("#FF0000"), lipgloss.Color("#0000FF")) +``` + +For more on borders see [the docs](https://pkg.go.dev/charm.land/lipgloss/v2#Border). ## Copying Styles @@ -233,12 +301,10 @@ style := lipgloss.NewStyle().Foreground(lipgloss.Color("219")) copiedStyle := style // this is a true copy wildStyle := style.Blink(true) // this is also true copy, with blink added - ``` -Since `Style` data structures contains only primitive types, assigning a style -to another effectively creates a new copy of the style without mutating the -original. +Since `Style` is a pure value type, assigning a style to another effectively +creates a new copy of the style without mutating the original. ## Inheritance @@ -269,7 +335,7 @@ var style = lipgloss.NewStyle(). UnsetBackground() // never mind ``` -When a rule is unset, it won't be inherited or copied. +When a rule is unset, it won’t be inherited or copied. ## Enforcing Rules @@ -302,50 +368,57 @@ style = style.TabWidth(0) // remove tabs entirely style = style.TabWidth(lipgloss.NoTabConversion) // leave tabs intact ``` +## Wrapping + +The `Wrap` function wraps text while preserving ANSI styles and hyperlinks +across line boundaries: + +```go +wrapped := lipgloss.Wrap(styledText, 40, " ") +``` + ## Rendering Generally, you just call the `Render(string...)` method on a `lipgloss.Style`: ```go style := lipgloss.NewStyle().Bold(true).SetString("Hello,") -fmt.Println(style.Render("kitty.")) // Hello, kitty. -fmt.Println(style.Render("puppy.")) // Hello, puppy. +lipgloss.Println(style.Render("kitty.")) // Hello, kitty. +lipgloss.Println(style.Render("puppy.")) // Hello, puppy. ``` But you could also use the Stringer interface: ```go var style = lipgloss.NewStyle().SetString("你好,猫咪。").Bold(true) -fmt.Println(style) // 你好,猫咪。 +lipgloss.Println(style) // 你好,猫咪。 ``` -### Custom Renderers +## Utilities -Custom renderers allow you to render to a specific outputs. This is -particularly important when you want to render to different outputs and -correctly detect the color profile and dark background status for each, such as -in a server-client situation. +In addition to pure styling, Lip Gloss also ships with some utilities to help +assemble your layouts. -```go -func myLittleHandler(sess ssh.Session) { - // Create a renderer for the client. - renderer := lipgloss.NewRenderer(sess) +### Compositing - // Create a new style on the renderer. - style := renderer.NewStyle().Background(lipgloss.AdaptiveColor{Light: "63", Dark: "228"}) +

xx

- // Render. The color profile and dark background state will be correctly detected. - io.WriteString(sess, style.Render("Heyyyyyyy")) -} -``` +Lip Gloss includes a powerful, cell-based compositor for rendering layered +content: -For an example on using a custom renderer over SSH with [Wish][wish] see the -[SSH example][ssh-example]. +```go +// Create some layers. +a := lipgloss.NewLayer(pickles).X(4).Y(2).Z(1) +b := lipgloss.NewLayer(bitterMelon).X(22).Y(1) +c := lipgloss.NewLayer(sriracha).X(11).Y(7) -## Utilities +// Composite 'em and render. +output := compositor.Compose(a, b, c).Render() +``` -In addition to pure styling, Lip Gloss also ships with some utilities to help -assemble your layouts. +For a more thorough example, see [the canvas +example](./examples/canvas/main.go). For reference, including how to detect +mouse clicks on layers, see [the docs][docs]. ### Joining Paragraphs @@ -383,9 +456,22 @@ height := lipgloss.Height(block) w, h := lipgloss.Size(block) ``` +### Blending Colors + +You can blend colors in one or two dimensions for gradient effects: + +```go +// 1-dimentinoal gradient +colors := lipgloss.Blend1D(10, lipgloss.Color("#FF0000"), lipgloss.Color("#0000FF")) + +// 2-dimensional gradient with rotation +colors := lipgloss.Blend2D(80, 24, 45.0, color1, color2, color3) +``` + ### Placing Text in Whitespace -Sometimes you’ll simply want to place a block of text in whitespace. +Sometimes you’ll simply want to place a block of text in whitespace. This is +a lightweight alternative to compositing. ```go // Center a paragraph horizontally in a space 80 cells wide. The height of @@ -407,7 +493,7 @@ You can also style the whitespace. For details, see [the docs][docs]. Lip Gloss ships with a table rendering sub-package. ```go -import "github.com/charmbracelet/lipgloss/table" +import "charm.land/lipgloss/v2/table" ``` Define some rows of data. @@ -459,14 +545,11 @@ t.Row("English", "You look absolutely fabulous.", "How's it going?") Print the table. ```go -fmt.Println(t) +lipgloss.Println(t) ``` ![Table Example](https://github.com/charmbracelet/lipgloss/assets/42545625/6e4b70c4-f494-45da-a467-bdd27df30d5d) -> [!WARNING] -> Table `Rows` need to be declared before `Offset` otherwise it does nothing. - ### Table Borders There are helpers to generate tables in markdown or ASCII style: @@ -503,14 +586,14 @@ table.New().Border(lipgloss.ASCIIBorder()) +----------+--------------+-----------+ ``` -For more on tables see [the docs](https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc) and [examples](https://github.com/charmbracelet/lipgloss/tree/master/examples/table). +For more on tables see [the docs][docs] and [examples](https://github.com/charmbracelet/lipgloss/tree/master/examples/table). ## Rendering Lists Lip Gloss ships with a list rendering sub-package. ```go -import "github.com/charmbracelet/lipgloss/list" +import "charm.land/lipgloss/v2/list" ``` Define a new list. @@ -522,7 +605,7 @@ l := list.New("A", "B", "C") Print the list. ```go -fmt.Println(l) +lipgloss.Println(l) // • A // • B @@ -548,7 +631,7 @@ l := list.New( Print the list. ```go -fmt.Println(l) +lipgloss.Println(l) ```

@@ -564,7 +647,7 @@ itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1 l := list.New( "Glossier", - "Claire’s Boutique", + "Claire's Boutique", "Nyx", "Mac", "Milk", @@ -617,7 +700,7 @@ for i := 0; i < repeat; i++ { Lip Gloss ships with a tree rendering sub-package. ```go -import "github.com/charmbracelet/lipgloss/tree" +import "charm.land/lipgloss/v2/tree" ``` Define a new tree. @@ -630,7 +713,7 @@ t := tree.Root("."). Print the tree. ```go -fmt.Println(t) +lipgloss.Println(t) // . // ├── A @@ -661,7 +744,7 @@ t := tree.Root("."). Print the tree. ```go -fmt.Println(t) +lipgloss.Println(t) ```

@@ -713,49 +796,159 @@ for i := 0; i < repeat; i++ { } ``` ---- +## Advanced Color Usage -## FAQ +One of the most powerful features of Lip Gloss is the ability to render +different colors at runtime depending on the user's terminal and environment, +allowing you to present the best possible user experience. -

- -Why are things misaligning? Why are borders at the wrong widths? - -

This is most likely due to your locale and encoding, particularly with -regard to Chinese, Japanese, and Korean (for example, zh_CN.UTF-8 -or ja_JP.UTF-8). The most direct way to fix this is to set -RUNEWIDTH_EASTASIAN=0 in your environment.

- -

For details see https://github.com/charmbracelet/lipgloss/issues/40.

-
+This section shows you how to do exactly that.
- -Why isn't Lip Gloss displaying colors? - -

Lip Gloss automatically degrades colors to the best available option in the -given terminal, and if output's not a TTY it will remove color output entirely. -This is common when running tests, CI, or when piping output elsewhere.

+Migrating from v1? -

If necessary, you can force a color profile in your tests with -SetColorProfile.

+The `compat` package provides `AdaptiveColor`, `CompleteColor`, and +`CompleteAdaptiveColor` for a quicker migration from v1. These work by +looking at `stdin` and `stdout` on a global basis: ```go -import ( - "github.com/charmbracelet/lipgloss" - "github.com/muesli/termenv" -) +import "charm.land/lipgloss/v2/compat" -lipgloss.SetColorProfile(termenv.TrueColor) +color := compat.AdaptiveColor{ + Light: lipgloss.Color("#f1f1f1"), + Dark: lipgloss.Color("#cccccc"), +} ``` -_Note:_ this option limits the flexibility of your application and can cause -ANSI escape codes to be output in cases where that might not be desired. Take -careful note of your use case and environment before choosing to force a color -profile. +Note that we don't recommend this for new code as it removes the purity from +Lip Gloss, computationally speaking, as it removes transparency around when +I/O happens, which could cause Lip Gloss to compete for resources (like stdin) +with other tools.
+### Adaptive Colors + +You can render different colors at runtime depending on whether the terminal +has a light or dark background: + +```go +hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stdout) +lightDark := lipgloss.LightDark(hasDarkBG) + +myColor := lightDark(lipgloss.Color("#D7FFAE"), lipgloss.Color("#D75FEE")) +``` + +#### With Bubble Tea + +In Bubble Tea, request the background color, listen for a +`BackgroundColorMsg`, and respond accordingly: + +```go +func (m model) Init() tea.Cmd { + // First, send a Cmd to request the terminal background color. + return tea.RequestBackgroundColor +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.BackgroundColorMsg: + // Great, we have the background color. Now we can set up our styles + // against the color. + m.styles = newStyles(msg.IsDark()) + return m, nil + } +} + +func newStyles(bgIsDark bool) styles { + // A little ternary function that will return the appropriate color + // based on the background color. + lightDark := lipgloss.LightDark(bgIsDark) + + return styles{ + myHotStyle: lipgloss.NewStyle().Foreground(lightDark( + lipgloss.Color("#f1f1f1"), + lipgloss.Color("#333333"), + )), + } +} +``` + +#### Standalone + +If you’re not using Bubble Tea you can perform the query manually: + +```go +// What's the background color? +hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stderr) + +// A helper function that will return the appropriate color based on the +// background. +lightDark := lipgloss.LightDark(hasDarkBG) + +// A couple colors with light and dark variants. +thisColor := lightDark(lipgloss.Color("#C5ADF9"), lipgloss.Color("#864EFF")) +thatColor := lightDark(lipgloss.Color("#37CD96"), lipgloss.Color("#22C78A")) + +a := lipgloss.NewStyle().Foreground(thisColor).Render("this") +b := lipgloss.NewStyle().Foreground(thatColor).Render("that") + +// Render the appropriate colors at runtime: +lipgloss.Fprintf(os.Stderr, "my fave colors are %s and %s", a, b) +``` + +### Complete Colors + +In some cases where you may want to specify exact values for each color profile +(ANSI 16, ANSI 156, and TrueColor). For these cases, use the `Complete` helper: + +```go +// You'll need the colorprofile package. +import "github.com/charmbracelet/colorprofile" + +// Get the color profile. +profile := colorprofile.Detect(os.Stdout, os.Environ()) + +// Create a function for rendering the appropriate color based on the profile. +var completeColor := lipgloss.Complete(profile) + +// Now we'll choose the appropriate color at runtime. +myColor := completeColor(ansiColor, ansi256Color, trueColor) +``` + +### Color Downsampling + +One of the best things about Lip Gloss is that it can automatically downsample +colors to the best available profile, stripping colors (and ANSI) entirely when +output is not a TTY. + +If you’re using Lip Gloss with Bubble Tea there’s nothing to do here: +downsampling is built into Bubble Tea v2. If you’re not using Bubble Tea, use +the Lip Gloss writer functions, which are a drop-in replacement for the `fmt` +package: + +```go +s := lipgloss.NewStyle() + .Foreground(lipgloss.Color("#EB4268")) + .Render("Hello!") + +// Downsample if needed and print to stdout. +lipgloss.Println(s) + +// Render to a variable. +downsampled := lipgloss.Sprint(s) + +// Print to stderr. +lipgloss.Fprint(os.Stderr, s) +``` + +The full set: `Print`, `Println`, `Printf`, `Fprint`, `Fprintln`, `Fprintf`, +`Sprint`, `Sprintln`, `Sprintf`. + +Need more control? Check out +[Colorprofile](https://github.com/charmbracelet/colorprofile), which Lip Gloss +uses under the hood. + ## What about [Bubble Tea][tea]? Lip Gloss doesn’t replace Bubble Tea. Rather, it is an excellent Bubble Tea @@ -765,16 +958,7 @@ instead of concerning yourself with low-level layout details. In simple terms, you can use Lip Gloss to help build your Bubble Tea views. -[tea]: https://github.com/charmbracelet/tea - -## Under the Hood - -Lip Gloss is built on the excellent [Termenv][termenv] and [Reflow][reflow] -libraries which deal with color and ANSI-aware text operations, respectively. -For many use cases Termenv and Reflow will be sufficient for your needs. - -[termenv]: https://github.com/muesli/termenv -[reflow]: https://github.com/muesli/reflow +[tea]: https://github.com/charmbracelet/bubbletea ## Rendering Markdown @@ -794,9 +978,8 @@ See [contributing][contribute]. We’d love to hear your thoughts on this project. Feel free to drop us a note! -- [Twitter](https://twitter.com/charmcli) -- [The Fediverse](https://mastodon.social/@charmcli) -- [Discord](https://charm.sh/chat) +- [Discord](https://charm.land/chat) +- [Matrix](https://charm.land/matrix) ## License @@ -804,12 +987,10 @@ We’d love to hear your thoughts on this project. Feel free to drop us a note! --- -Part of [Charm](https://charm.sh). +Part of [Charm](https://charm.land). -The Charm logo +The Charm logo Charm热爱开源 • Charm loves open source -[docs]: https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc -[wish]: https://github.com/charmbracelet/wish -[ssh-example]: examples/ssh +[docs]: https://pkg.go.dev/charm.land/lipgloss/v2?tab=doc diff --git a/vendor/github.com/charmbracelet/lipgloss/Taskfile.yaml b/vendor/charm.land/lipgloss/v2/Taskfile.yaml similarity index 72% rename from vendor/github.com/charmbracelet/lipgloss/Taskfile.yaml rename to vendor/charm.land/lipgloss/v2/Taskfile.yaml index 0b4a7711..84fcf6c6 100644 --- a/vendor/github.com/charmbracelet/lipgloss/Taskfile.yaml +++ b/vendor/charm.land/lipgloss/v2/Taskfile.yaml @@ -1,6 +1,6 @@ # https://taskfile.dev -version: '3' +version: "3" tasks: lint: @@ -17,3 +17,8 @@ tasks: desc: Run table tests cmds: - go test ./table {{.CLI_ARGS}} + + test:tree: + desc: Run tree tests + cmds: + - go test ./tree {{.CLI_ARGS}} diff --git a/vendor/charm.land/lipgloss/v2/UPGRADE_GUIDE_V2.md b/vendor/charm.land/lipgloss/v2/UPGRADE_GUIDE_V2.md new file mode 100644 index 00000000..8f123f19 --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/UPGRADE_GUIDE_V2.md @@ -0,0 +1,504 @@ +# Lip Gloss v2 Upgrade Guide + +This guide covers migrating from Lip Gloss v1 (`github.com/charmbracelet/lipgloss`) +to Lip Gloss v2 (`charm.land/lipgloss/v2`). It is written for both humans and +LLMs performing automated migrations. + +--- + +## Table of Contents + +1. [Quick Start](#quick-start) +2. [Module Path](#module-path) +3. [Color System](#color-system) +4. [Renderer Removal](#renderer-removal) +5. [Printing and Color Downsampling](#printing-and-color-downsampling) +6. [Background Detection and Adaptive Colors](#background-detection-and-adaptive-colors) +7. [Whitespace Options](#whitespace-options) +8. [Underline](#underline) +9. [Style API Changes](#style-api-changes) +10. [Tree Subpackage](#tree-subpackage) +11. [Removed APIs](#removed-apis) +12. [Quick Reference Table](#quick-reference-table) + +--- + +## Quick Start + +For the fastest possible upgrade, do these two things: + +### 1. Use the `compat` package for adaptive/complete colors + +```go +import "charm.land/lipgloss/v2/compat" + +// v1 +color := lipgloss.AdaptiveColor{Light: "#f1f1f1", Dark: "#cccccc"} + +// v2 +color := compat.AdaptiveColor{Light: lipgloss.Color("#f1f1f1"), Dark: lipgloss.Color("#cccccc")} +``` + +The `compat` package reads `stdin`/`stdout` globally, just like v1. To +customize: + +```go +import ( + "charm.land/lipgloss/v2/compat" + "github.com/charmbracelet/colorprofile" +) + +func init() { + compat.HasDarkBackground = lipgloss.HasDarkBackground(os.Stdin, os.Stderr) + compat.Profile = colorprofile.Detect(os.Stderr, os.Environ()) +} +``` + +### 2. Use Lip Gloss writers for output + +```go +// v1 +fmt.Println(s) + +// v2 +lipgloss.Println(s) +``` + +This ensures colors are automatically downsampled. If you're using Bubble Tea +v2, this step is unnecessary — Bubble Tea handles it for you. + +**That's the quick path.** Read on for the full migration details. + +--- + +## Module Path + +The import path has changed. + +```go +// v1 +import "github.com/charmbracelet/lipgloss" + +// v2 +import "charm.land/lipgloss/v2" +``` + +**Install:** + +```bash +go get charm.land/lipgloss/v2 +``` + +All subpackages follow the same pattern: + +```go +// v1 +import "github.com/charmbracelet/lipgloss/table" +import "github.com/charmbracelet/lipgloss/tree" +import "github.com/charmbracelet/lipgloss/list" + +// v2 +import "charm.land/lipgloss/v2/table" +import "charm.land/lipgloss/v2/tree" +import "charm.land/lipgloss/v2/list" +``` + +**Search-and-replace pattern:** + +``` +github.com/charmbracelet/lipgloss → charm.land/lipgloss/v2 +``` + +--- + +## Color System + +This is the most significant API change. + +### `Color` is now a function, not a type + +```go +// v1 — Color is a string type +var c lipgloss.Color = "21" +var c lipgloss.Color = "#ff00ff" + +// v2 — Color is a function returning color.Color +var c color.Color = lipgloss.Color("21") +var c color.Color = lipgloss.Color("#ff00ff") +``` + +The return type is `image/color.Color` (from the standard library). + +### `TerminalColor` interface is removed + +All methods that accepted `lipgloss.TerminalColor` now accept +`image/color.Color`: + +```go +// v1 +func (s Style) Foreground(c TerminalColor) Style +func (s Style) Background(c TerminalColor) Style +func (s Style) BorderForeground(c ...TerminalColor) Style + +// v2 +func (s Style) Foreground(c color.Color) Style +func (s Style) Background(c color.Color) Style +func (s Style) BorderForeground(c ...color.Color) Style +``` + +**Migration:** Replace every `lipgloss.TerminalColor` with `color.Color` and +add `import "image/color"`. + +### `ANSIColor` is now an alias + +```go +// v1 — custom uint type +type ANSIColor uint + +// v2 — alias for ansi.IndexedColor +type ANSIColor = ansi.IndexedColor +``` + +v2 also exports named constants for the 16 basic ANSI colors: + +```go +lipgloss.Black, lipgloss.Red, lipgloss.Green, lipgloss.Yellow, +lipgloss.Blue, lipgloss.Magenta, lipgloss.Cyan, lipgloss.White, +lipgloss.BrightBlack, lipgloss.BrightRed, lipgloss.BrightGreen, +lipgloss.BrightYellow, lipgloss.BrightBlue, lipgloss.BrightMagenta, +lipgloss.BrightCyan, lipgloss.BrightWhite +``` + +### `AdaptiveColor`, `CompleteColor`, `CompleteAdaptiveColor` + +These types have been moved out of the root package. Use the `compat` package +for a drop-in replacement, or use the new `LightDark` and `Complete` helpers +for explicit control: + +```go +// v1 +color := lipgloss.AdaptiveColor{Light: "#0000ff", Dark: "#000099"} + +// v2 — using compat (quick path) +color := compat.AdaptiveColor{ + Light: lipgloss.Color("#0000ff"), + Dark: lipgloss.Color("#000099"), +} + +// v2 — using LightDark (recommended) +hasDark := lipgloss.HasDarkBackground(os.Stdin, os.Stdout) +lightDark := lipgloss.LightDark(hasDark) +color := lightDark(lipgloss.Color("#0000ff"), lipgloss.Color("#000099")) +``` + +```go +// v1 +color := lipgloss.CompleteColor{TrueColor: "#ff00ff", ANSI256: "200", ANSI: "5"} + +// v2 — using compat +color := compat.CompleteColor{ + TrueColor: lipgloss.Color("#ff00ff"), + ANSI256: lipgloss.Color("200"), + ANSI: lipgloss.Color("5"), +} + +// v2 — using Complete (recommended) +profile := colorprofile.Detect(os.Stdout, os.Environ()) +complete := lipgloss.Complete(profile) +color := complete(lipgloss.Color("5"), lipgloss.Color("200"), lipgloss.Color("#ff00ff")) +``` + +Note that `compat.AdaptiveColor` and friends take `color.Color` values for +their fields, not strings. + +--- + +## Renderer Removal + +The `Renderer` type and all associated functions are removed. In v1, every +`Style` carried a `*Renderer` pointer and the package maintained a global +default renderer. + +```go +// v1 — these no longer exist +lipgloss.DefaultRenderer() +lipgloss.SetDefaultRenderer(r) +lipgloss.NewRenderer(w, opts...) +lipgloss.ColorProfile() +lipgloss.SetColorProfile(p) +renderer.NewStyle() +``` + +**In v2, `Style` is a plain value type.** There is no renderer. Color +downsampling is handled at the output layer (see next section). + +**Migration:** + +- Replace `lipgloss.DefaultRenderer().NewStyle()` with `lipgloss.NewStyle()`. +- Replace `renderer.NewStyle()` with `lipgloss.NewStyle()`. +- Remove any `*Renderer` fields from your types. +- Remove calls to `SetColorProfile` — use `colorprofile.Detect` at the output + layer instead. + +--- + +## Printing and Color Downsampling + +In v1, color downsampling happened inside `Style.Render()` via the renderer. In +v2, `Render()` always emits full-fidelity ANSI. Downsampling happens when you +print. + +### Standalone Usage + +Use the Lip Gloss writer functions: + +```go +s := someStyle.Render("Hello!") + +// Print to stdout with automatic downsampling +lipgloss.Println(s) + +// Print to stderr +lipgloss.Fprintln(os.Stderr, s) + +// Render to a string (downsampled for stdout's profile) +str := lipgloss.Sprint(s) +``` + +The default writer targets `stdout`. To customize: + +```go +lipgloss.Writer = colorprofile.NewWriter(os.Stderr, os.Environ()) +``` + +### With Bubble Tea + +No changes needed. Bubble Tea v2 handles downsampling internally. + +--- + +## Background Detection and Adaptive Colors + +### Standalone + +v1 detected the background color automatically via the global renderer. v2 +requires explicit queries: + +```go +// v1 +hasDark := lipgloss.HasDarkBackground() + +// v2 — specify the input and output +hasDark := lipgloss.HasDarkBackground(os.Stdin, os.Stdout) +``` + +Then use `LightDark` to pick colors: + +```go +lightDark := lipgloss.LightDark(hasDark) +fg := lightDark(lipgloss.Color("#333333"), lipgloss.Color("#f1f1f1")) + +s := lipgloss.NewStyle().Foreground(fg) +``` + +### With Bubble Tea + +Request the background color in `Init` and listen for the response: + +```go +func (m model) Init() tea.Cmd { + return tea.RequestBackgroundColor +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.BackgroundColorMsg: + m.styles = newStyles(msg.IsDark()) + } + // ... +} + +func newStyles(bgIsDark bool) styles { + lightDark := lipgloss.LightDark(bgIsDark) + return styles{ + title: lipgloss.NewStyle().Foreground(lightDark( + lipgloss.Color("#333333"), + lipgloss.Color("#f1f1f1"), + )), + } +} +``` + +--- + +## Whitespace Options + +The separate foreground/background whitespace options have been replaced by a +single style option: + +```go +// v1 +lipgloss.Place(width, height, hPos, vPos, str, + lipgloss.WithWhitespaceForeground(lipgloss.Color("#333")), + lipgloss.WithWhitespaceBackground(lipgloss.Color("#000")), +) + +// v2 +lipgloss.Place(width, height, hPos, vPos, str, + lipgloss.WithWhitespaceStyle(lipgloss.NewStyle(). + Foreground(lipgloss.Color("#333")). + Background(lipgloss.Color("#000")), + ), +) +``` + +--- + +## Underline + +`Underline(bool)` still works for basic on/off. v2 adds fine-grained control: + +```go +// v1 +s := lipgloss.NewStyle().Underline(true) + +// v2 — still works +s := lipgloss.NewStyle().Underline(true) + +// v2 — new: specific styles +s := lipgloss.NewStyle().UnderlineStyle(lipgloss.UnderlineCurly) + +// v2 — new: colored underlines +s := lipgloss.NewStyle(). + UnderlineStyle(lipgloss.UnderlineSingle). + UnderlineColor(lipgloss.Color("#FF0000")) +``` + +Internally, `Underline(true)` is equivalent to `UnderlineStyle(UnderlineSingle)` +and `Underline(false)` is equivalent to `UnderlineStyle(UnderlineNone)`. + +--- + +## Style API Changes + +### `NewStyle()` is no longer tied to a Renderer + +```go +// v1 +s := lipgloss.NewStyle() // uses global renderer +s := renderer.NewStyle() // uses specific renderer + +// v2 +s := lipgloss.NewStyle() // pure value, no renderer +``` + +### Color getters return `color.Color` + +```go +// v1 +fg := s.GetForeground() // returns TerminalColor + +// v2 +fg := s.GetForeground() // returns color.Color +``` + +### New style methods + +| Method | Description | +|---|---| +| `UnderlineStyle(Underline)` | Set underline style (single, double, curly, etc.) | +| `UnderlineColor(color.Color)` | Set underline color | +| `PaddingChar(rune)` | Set the character used for padding fill | +| `MarginChar(rune)` | Set the character used for margin fill | +| `Hyperlink(link, params...)` | Set a clickable hyperlink | +| `BorderForegroundBlend(...color.Color)` | Apply gradient colors to borders | +| `BorderForegroundBlendOffset(int)` | Set the offset for border gradient | + +Each has a corresponding `Get*`, `Unset*`, and where applicable `Get*` +accessor. + +--- + +## Tree Subpackage + +The import path changes and there are new styling options: + +```go +// v1 +import "github.com/charmbracelet/lipgloss/tree" + +// v2 +import "charm.land/lipgloss/v2/tree" +``` + +New methods: + +- `IndenterStyle(lipgloss.Style)` — set a static style for tree indentation. +- `IndenterStyleFunc(func(Children, int) lipgloss.Style)` — conditionally style + indentation. +- `Width(int)` — set tree width for padding. + +--- + +## Removed APIs + +The following types and functions no longer exist in v2. This table shows each +removed symbol and its replacement. + +| v1 Symbol | v2 Replacement | +|---|---| +| `type Renderer` | Removed entirely | +| `DefaultRenderer()` | Not needed | +| `SetDefaultRenderer(r)` | Not needed | +| `NewRenderer(w, opts...)` | Not needed | +| `ColorProfile()` | `colorprofile.Detect(w, env)` | +| `SetColorProfile(p)` | Set `lipgloss.Writer.Profile` | +| `HasDarkBackground()` (no args) | `lipgloss.HasDarkBackground(in, out)` | +| `SetHasDarkBackground(b)` | Not needed — pass bool to `LightDark` | +| `type TerminalColor` | `image/color.Color` | +| `type Color string` | `func Color(string) color.Color` | +| `type ANSIColor uint` | `type ANSIColor = ansi.IndexedColor` | +| `type AdaptiveColor` | `compat.AdaptiveColor` or `LightDark` | +| `type CompleteColor` | `compat.CompleteColor` or `Complete` | +| `type CompleteAdaptiveColor` | `compat.CompleteAdaptiveColor` | +| `WithWhitespaceForeground(c)` | `WithWhitespaceStyle(s)` | +| `WithWhitespaceBackground(c)` | `WithWhitespaceStyle(s)` | +| `renderer.NewStyle()` | `lipgloss.NewStyle()` | + +--- + +## Quick Reference Table + +A side-by-side summary for common patterns: + +| Task | v1 | v2 | +|---|---|---| +| Import | `"github.com/charmbracelet/lipgloss"` | `"charm.land/lipgloss/v2"` | +| Create style | `lipgloss.NewStyle()` | `lipgloss.NewStyle()` | +| Hex color | `lipgloss.Color("#ff00ff")` | `lipgloss.Color("#ff00ff")` | +| ANSI color | `lipgloss.Color("5")` | `lipgloss.Color("5")` or `lipgloss.Magenta` | +| Adaptive color | `lipgloss.AdaptiveColor{Light: "#fff", Dark: "#000"}` | `compat.AdaptiveColor{Light: lipgloss.Color("#fff"), Dark: lipgloss.Color("#000")}` | +| Set foreground | `s.Foreground(lipgloss.Color("5"))` | `s.Foreground(lipgloss.Color("5"))` | +| Print with downsampling | `fmt.Println(s.Render("hi"))` | `lipgloss.Println(s.Render("hi"))` | +| Detect dark bg | `lipgloss.HasDarkBackground()` | `lipgloss.HasDarkBackground(os.Stdin, os.Stdout)` | +| Light/dark color | `lipgloss.AdaptiveColor{...}` | `lipgloss.LightDark(isDark)(light, dark)` | +| Whitespace styling | `WithWhitespaceForeground(c)` | `WithWhitespaceStyle(lipgloss.NewStyle().Foreground(c))` | +| Underline | `s.Underline(true)` | `s.Underline(true)` or `s.UnderlineStyle(lipgloss.UnderlineCurly)` | + +--- + +## Feedback + +Questions, issues, or feedback: + +- [Discord](https://charm.land/discord) +- [Matrix](https://charm.land/matrix) +- [Email](mailto:vt100@charm.land) + +--- + +Part of [Charm](https://charm.land). + +The Charm logo + +Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة diff --git a/vendor/github.com/charmbracelet/lipgloss/align.go b/vendor/charm.land/lipgloss/v2/align.go similarity index 89% rename from vendor/github.com/charmbracelet/lipgloss/align.go rename to vendor/charm.land/lipgloss/v2/align.go index ce654b23..d196213b 100644 --- a/vendor/github.com/charmbracelet/lipgloss/align.go +++ b/vendor/charm.land/lipgloss/v2/align.go @@ -4,13 +4,12 @@ import ( "strings" "github.com/charmbracelet/x/ansi" - "github.com/muesli/termenv" ) // Perform text alignment. If the string is multi-lined, we also make all lines -// the same width by padding them with spaces. If a termenv style is passed, -// use that to style the spaces added. -func alignTextHorizontal(str string, pos Position, width int, style *termenv.Style) string { +// the same width by padding them with spaces. If a style is passed, use that +// to style the spaces added. +func alignTextHorizontal(str string, pos Position, width int, style *ansi.Style) string { lines, widestLine := getLines(str) var b strings.Builder @@ -21,7 +20,7 @@ func alignTextHorizontal(str string, pos Position, width int, style *termenv.Sty shortAmount += max(0, width-(shortAmount+lineWidth)) // difference from the total width, if set if shortAmount > 0 { - switch pos { //nolint:exhaustive + switch pos { case Right: s := strings.Repeat(" ", shortAmount) if style != nil { @@ -59,7 +58,7 @@ func alignTextHorizontal(str string, pos Position, width int, style *termenv.Sty return b.String() } -func alignTextVertical(str string, pos Position, height int, _ *termenv.Style) string { +func alignTextVertical(str string, pos Position, height int, _ *ansi.Style) string { strHeight := strings.Count(str, "\n") + 1 if height < strHeight { return str diff --git a/vendor/charm.land/lipgloss/v2/ansi_unix.go b/vendor/charm.land/lipgloss/v2/ansi_unix.go new file mode 100644 index 00000000..b4fca7d1 --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/ansi_unix.go @@ -0,0 +1,8 @@ +//go:build !windows + +package lipgloss + +import "os" + +// EnableLegacyWindowsANSI is only needed on Windows. +func EnableLegacyWindowsANSI(*os.File) {} diff --git a/vendor/charm.land/lipgloss/v2/ansi_windows.go b/vendor/charm.land/lipgloss/v2/ansi_windows.go new file mode 100644 index 00000000..8a1ef00e --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/ansi_windows.go @@ -0,0 +1,30 @@ +//go:build windows + +package lipgloss + +import ( + "os" + + "golang.org/x/sys/windows" +) + +// EnableLegacyWindowsANSI enables support for ANSI color sequences in the +// Windows default console (cmd.exe and the PowerShell application). Note that +// this only works with Windows 10 and greater. Also note that Windows Terminal +// supports colors by default. +func EnableLegacyWindowsANSI(f *os.File) { + var mode uint32 + handle := windows.Handle(f.Fd()) + err := windows.GetConsoleMode(handle, &mode) + if err != nil { + return + } + + // See https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences + if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING { + vtpmode := mode | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + if err := windows.SetConsoleMode(handle, vtpmode); err != nil { + return + } + } +} diff --git a/vendor/charm.land/lipgloss/v2/blending.go b/vendor/charm.land/lipgloss/v2/blending.go new file mode 100644 index 00000000..82830f3e --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/blending.go @@ -0,0 +1,196 @@ +package lipgloss + +import ( + "image/color" + "math" + "slices" + + "github.com/lucasb-eyer/go-colorful" +) + +// Blend1D blends a series of colors together in one linear dimension using multiple +// stops, into the provided number of steps. Uses the "CIE L*, a*, b*" (CIELAB) color-space. +// +// Note that if any of the provided colors are completely transparent, we will +// assume that the alpha value was lost in conversion from RGB -> RGBA, and we +// will set the alpha to opaque, as it's not possible to blend something completely +// transparent. +func Blend1D(steps int, stops ...color.Color) []color.Color { + if steps < 0 { + steps = 0 + } + + if steps <= len(stops) { + return stops[:steps] + } + + // Ensure they didn't provide any nil colors. + stops = slices.DeleteFunc(stops, func(c color.Color) bool { + return c == nil + }) + + if len(stops) == 0 { + return nil // We can't safely fallback. + } + + // If they only provided one valid color (or some nil colors), we will just return + // an array of that color, for the amount of steps they requested. + if len(stops) == 1 { + singleColor := stops[0] + result := make([]color.Color, steps) + for i := range result { + result[i] = singleColor + } + return result + } + + blended := make([]color.Color, steps) + + // Convert stops to colorful.Color once + cstops := make([]colorful.Color, len(stops)) + for i, k := range stops { + cstops[i], _ = colorful.MakeColor(ensureNotTransparent(k)) + } + + numSegments := len(cstops) - 1 + defaultSize := steps / numSegments + remainingSteps := steps % numSegments + + resultIndex := 0 + for i := range numSegments { + from := cstops[i] + to := cstops[i+1] + + // Calculate segment size. + segmentSize := defaultSize + if i < remainingSteps { + segmentSize++ + } + + divisor := float64(segmentSize - 1) + + // Generate colors for this segment. + for j := 0; j < segmentSize; j++ { + var blendingFactor float64 + if segmentSize > 1 { + blendingFactor = float64(j) / divisor + } + blended[resultIndex] = from.BlendLab(to, blendingFactor).Clamped() + resultIndex++ + } + } + + return blended +} + +// Blend2D blends a series of colors together in two linear dimensions using +// multiple stops, into the provided width/height. Uses the "CIE L*, a*, b*" (CIELAB) +// color-space. The angle parameter controls the rotation of the gradient (0-360°), +// where 0° is left-to-right, 45° is bottom-left to top-right (diagonal). The function +// returns colors in a 1D row-major order ([row1, row2, row3, ...]). +// +// Example of how to iterate over the result: +// +// gradient := colors.Blend2D(width, height, 180, color1, color2, color3, ...) +// gradientContent := strings.Builder{} +// for y := range height { +// for x := range width { +// index := y*width + x +// gradientContent.WriteString( +// lipgloss.NewStyle(). +// Background(gradient[index]). +// Render(" "), +// ) +// } +// if y < height-1 { // End of row. +// gradientContent.WriteString("\n") +// } +// } +// +// Note that if any of the provided colors are completely transparent, we will +// assume that the alpha value was lost in conversion from RGB -> RGBA, and we +// will set the alpha to opaque, as it's not possible to blend something completely +// transparent. +func Blend2D(width, height int, angle float64, stops ...color.Color) []color.Color { + if width < 1 { + width = 1 + } + if height < 1 { + height = 1 + } + + // Normalize angle to 0-360. + angle = math.Mod(angle, 360) + if angle < 0 { + angle += 360 + } + + // Ensure they didn't provide any nil colors. + stops = slices.DeleteFunc(stops, func(c color.Color) bool { + return c == nil + }) + + if len(stops) == 0 { + return nil // We can't safely fallback. + } + + // If they only provided one valid color (or some nil colors), we will just return + // an array of that color, for the amount of pixels they requested. + if len(stops) == 1 { + singleColor := stops[0] + result := make([]color.Color, width*height) + for i := range result { + result[i] = singleColor + } + return result + } + + // For 2D blending, we'll create a gradient along the diagonal and then sample + // from it based on the angle. We'll use the maximum dimension to ensure we have + // enough resolution for the gradient. + diagonalGradient := Blend1D(max(width, height), stops...) + + result := make([]color.Color, width*height) + + // Calculate center point for rotation. + centerX := float64(width-1) / 2.0 + centerY := float64(height-1) / 2.0 + + angleRad := angle * math.Pi / 180.0 // -> radians. + + // Pre-calculate sin and cos. + cosAngle := math.Cos(angleRad) + sinAngle := math.Sin(angleRad) + + // Calculate diagonal length for proper gradient mapping. + diagonalLength := math.Sqrt(float64(width*width + height*height)) + + // Pre-calculate gradient length for index calculation. + gradientLen := float64(len(diagonalGradient) - 1) + + for y := range height { + // Calculate the distance from center along the gradient direction. + dy := float64(y) - centerY + + for x := 0; x < width; x++ { + // Calculate the distance from center along the gradient direction. + dx := float64(x) - centerX + + rotX := dx*cosAngle - dy*sinAngle // Rotate the point by the angle. + + // Map the rotated position to the gradient. Normalize to 0-1 range based on + // the diagonal length. + gradientPos := clamp((rotX+diagonalLength/2.0)/diagonalLength, 0, 1) + + // Calculate the index in the gradient. + gradientIndex := int(gradientPos * gradientLen) + if gradientIndex >= len(diagonalGradient) { + gradientIndex = len(diagonalGradient) - 1 + } + + result[y*width+x] = diagonalGradient[gradientIndex] // -> row-major order. + } + } + + return result +} diff --git a/vendor/github.com/charmbracelet/lipgloss/borders.go b/vendor/charm.land/lipgloss/v2/borders.go similarity index 73% rename from vendor/github.com/charmbracelet/lipgloss/borders.go rename to vendor/charm.land/lipgloss/v2/borders.go index b36f8743..7f12360f 100644 --- a/vendor/github.com/charmbracelet/lipgloss/borders.go +++ b/vendor/charm.land/lipgloss/v2/borders.go @@ -1,10 +1,13 @@ package lipgloss import ( + "image/color" + "slices" "strings" + "unicode/utf8" "github.com/charmbracelet/x/ansi" - "github.com/muesli/termenv" + "github.com/clipperhouse/displaywidth" "github.com/rivo/uniseg" ) @@ -56,10 +59,7 @@ func (b Border) GetLeftSize() int { func getBorderEdgeWidth(borderParts ...string) (maxWidth int) { for _, piece := range borderParts { - w := maxRuneWidth(piece) - if w > maxWidth { - maxWidth = w - } + maxWidth = max(maxWidth, maxRuneWidth(piece)) } return maxWidth } @@ -278,6 +278,52 @@ func ASCIIBorder() Border { return asciiBorder } +type borderBlend struct { + topGradient []color.Color + rightGradient []color.Color + bottomGradient []color.Color + leftGradient []color.Color +} + +func (s Style) borderBlend(width, height int, colors ...color.Color) *borderBlend { + gradient := Blend1D( + (height+width+2)*2, + colors..., + ) + + // Rotate array forward or reverse based on the offset if provided. + if r := -s.getAsInt(borderForegroundBlendOffsetKey); r != 0 { + n := len(gradient) + r %= n + if r < 0 { + r += n + } + slices.Reverse(gradient[:r]) + slices.Reverse(gradient[r:]) + slices.Reverse(gradient) + } + + offset := 0 + getFromOffset := func(size int) (s []color.Color) { + s = gradient[offset : offset+size] + offset += size + return s + } + + blend := &borderBlend{ + topGradient: getFromOffset(width + 2), + rightGradient: getFromOffset(height), + bottomGradient: getFromOffset(width + 2), + leftGradient: getFromOffset(height), + } + + // bottom and left gradients are reversed because they are drawn in reverse order. + slices.Reverse(blend.bottomGradient) + slices.Reverse(blend.leftGradient) + + return blend +} + func (s Style) applyBorder(str string) string { var ( border = s.getBorderStyle() @@ -285,21 +331,11 @@ func (s Style) applyBorder(str string) string { hasRight = s.getAsBool(borderRightKey, false) hasBottom = s.getAsBool(borderBottomKey, false) hasLeft = s.getAsBool(borderLeftKey, false) - - topFG = s.getAsColor(borderTopForegroundKey) - rightFG = s.getAsColor(borderRightForegroundKey) - bottomFG = s.getAsColor(borderBottomForegroundKey) - leftFG = s.getAsColor(borderLeftForegroundKey) - - topBG = s.getAsColor(borderTopBackgroundKey) - rightBG = s.getAsColor(borderRightBackgroundKey) - bottomBG = s.getAsColor(borderBottomBackgroundKey) - leftBG = s.getAsColor(borderLeftBackgroundKey) ) // If a border is set and no sides have been specifically turned on or off // render borders on all sides. - if s.implicitBorders() { + if s.isBorderStyleSetWithoutSides() { hasTop = true hasRight = true hasBottom = true @@ -320,8 +356,11 @@ func (s Style) applyBorder(str string) string { width += maxRuneWidth(border.Left) } - if hasRight && border.Right == "" { - border.Right = " " + if hasRight { + if border.Right == "" { + border.Right = " " + } + width += maxRuneWidth(border.Right) } // If corners should be rendered but are set with the empty string, fill them @@ -370,13 +409,35 @@ func (s Style) applyBorder(str string) string { border.BottomRight = getFirstRuneAsString(border.BottomRight) border.BottomLeft = getFirstRuneAsString(border.BottomLeft) + var topFG, rightFG, bottomFG, leftFG color.Color + var ( + blendFG = s.getAsColors(borderForegroundBlendKey) + topBG = s.getAsColor(borderTopBackgroundKey) + rightBG = s.getAsColor(borderRightBackgroundKey) + bottomBG = s.getAsColor(borderBottomBackgroundKey) + leftBG = s.getAsColor(borderLeftBackgroundKey) + ) + + var blend *borderBlend + if len(blendFG) > 0 { + blend = s.borderBlend(width, len(lines), blendFG...) + } else { + topFG = s.getAsColor(borderTopForegroundKey) + rightFG = s.getAsColor(borderRightForegroundKey) + bottomFG = s.getAsColor(borderBottomForegroundKey) + leftFG = s.getAsColor(borderLeftForegroundKey) + } + var out strings.Builder // Render top if hasTop { top := renderHorizontalEdge(border.TopLeft, border.Top, border.TopRight, width) - top = s.styleBorder(top, topFG, topBG) - out.WriteString(top) + if blend != nil { + out.WriteString(s.styleBorderBlend(top, blend.topGradient, topBG)) + } else { + out.WriteString(s.styleBorder(top, topFG, topBG)) + } out.WriteRune('\n') } @@ -387,23 +448,32 @@ func (s Style) applyBorder(str string) string { rightIndex := 0 // Render sides + var r string for i, l := range lines { if hasLeft { - r := string(leftRunes[leftIndex]) + r = string(leftRunes[leftIndex]) leftIndex++ if leftIndex >= len(leftRunes) { leftIndex = 0 } - out.WriteString(s.styleBorder(r, leftFG, leftBG)) + if blend != nil { + out.WriteString(s.styleBorder(r, blend.leftGradient[i], leftBG)) + } else { + out.WriteString(s.styleBorder(r, leftFG, leftBG)) + } } out.WriteString(l) if hasRight { - r := string(rightRunes[rightIndex]) + r = string(rightRunes[rightIndex]) rightIndex++ if rightIndex >= len(rightRunes) { rightIndex = 0 } - out.WriteString(s.styleBorder(r, rightFG, rightBG)) + if blend != nil { + out.WriteString(s.styleBorder(r, blend.rightGradient[i], rightBG)) + } else { + out.WriteString(s.styleBorder(r, rightFG, rightBG)) + } } if i < len(lines)-1 { out.WriteRune('\n') @@ -413,9 +483,12 @@ func (s Style) applyBorder(str string) string { // Render bottom if hasBottom { bottom := renderHorizontalEdge(border.BottomLeft, border.Bottom, border.BottomRight, width) - bottom = s.styleBorder(bottom, bottomFG, bottomBG) out.WriteRune('\n') - out.WriteString(bottom) + if blend != nil { + out.WriteString(s.styleBorderBlend(bottom, blend.bottomGradient, bottomBG)) + } else { + out.WriteString(s.styleBorder(bottom, bottomFG, bottomBG)) + } } return out.String() @@ -435,49 +508,73 @@ func renderHorizontalEdge(left, middle, right string, width int) string { out := strings.Builder{} out.WriteString(left) - for i := leftWidth + rightWidth; i < width+rightWidth; { - out.WriteRune(runes[j]) + + for i := 0; i < width-leftWidth-rightWidth; { + r := runes[j] + out.WriteRune(r) + i += ansi.StringWidth(string(r)) j++ if j >= len(runes) { j = 0 } - i += ansi.StringWidth(string(runes[j])) } - out.WriteString(right) + out.WriteString(right) return out.String() } -// Apply foreground and background styling to a border. -func (s Style) styleBorder(border string, fg, bg TerminalColor) string { +// styleBorder applies foreground and background styling to a border. +func (s Style) styleBorder(border string, fg, bg color.Color) string { if fg == noColor && bg == noColor { return border } - - style := termenv.Style{} - + var style ansi.Style if fg != noColor { - style = style.Foreground(fg.color(s.r)) + style = style.ForegroundColor(fg) } if bg != noColor { - style = style.Background(bg.color(s.r)) + style = style.BackgroundColor(bg) } - return style.Styled(border) } +// styleBorderBlend applies foreground and background styling to a border, using blending. +func (s Style) styleBorderBlend(border string, fg []color.Color, bg color.Color) string { + var out strings.Builder + var style ansi.Style + var i int + + gr := uniseg.NewGraphemes(border) + for gr.Next() { + style = style[:0] + if fg[i] != noColor { + style = style.ForegroundColor(fg[i]) + } + if bg != noColor { + style = style.BackgroundColor(bg) + } + _, _ = out.WriteString(style.String()) + _, _ = out.Write(gr.Bytes()) + i++ + } + _, _ = out.WriteString(ansi.ResetStyle) + return out.String() +} + func maxRuneWidth(str string) int { + switch len(str) { + case 0: + return 0 + case 1: + return displaywidth.String(str) + } + var width int - state := -1 - for len(str) > 0 { - var w int - _, str, w, state = uniseg.FirstGraphemeClusterInString(str, state) - if w > width { - width = w - } + g := displaywidth.StringGraphemes(str) + for g.Next() { + width = max(width, g.Width()) } - return width } @@ -485,6 +582,6 @@ func getFirstRuneAsString(str string) string { if str == "" { return str } - r := []rune(str) - return string(r[0]) + _, size := utf8.DecodeRuneInString(str) + return str[:size] } diff --git a/vendor/charm.land/lipgloss/v2/canvas.go b/vendor/charm.land/lipgloss/v2/canvas.go new file mode 100644 index 00000000..e6cc3b45 --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/canvas.go @@ -0,0 +1,89 @@ +package lipgloss + +import ( + uv "github.com/charmbracelet/ultraviolet" + "github.com/charmbracelet/x/ansi" +) + +// Canvas is a cell-buffer that can be used to compose and draw [uv.Drawable]s +// like [Layer]s. +// +// Composed drawables are drawn onto the canvas in the order they were +// composed, meaning later drawables will appear "on top" of earlier ones. +// +// A canvas can read, modify, and render its cell contents. +// +// It implements [uv.Screen] and [uv.Drawable]. +type Canvas struct { + scr uv.ScreenBuffer +} + +var _ uv.Screen = (*Canvas)(nil) + +// NewCanvas creates a new [Canvas] with the given size. +func NewCanvas(width, height int) *Canvas { + c := new(Canvas) + c.scr = uv.NewScreenBuffer(width, height) + c.scr.Method = ansi.GraphemeWidth + return c +} + +// Resize resizes the canvas to the given width and height. +func (c *Canvas) Resize(width, height int) { + c.scr.Resize(width, height) +} + +// Clear clears the canvas. +func (c *Canvas) Clear() { + c.scr.Clear() +} + +// Bounds implements [uv.Screen]. +func (c *Canvas) Bounds() uv.Rectangle { + return c.scr.Bounds() +} + +// Width returns the width of the canvas. +func (c *Canvas) Width() int { + return c.scr.Width() +} + +// Height returns the height of the canvas. +func (c *Canvas) Height() int { + return c.scr.Height() +} + +// CellAt implements [uv.Screen]. +func (c *Canvas) CellAt(x int, y int) *uv.Cell { + return c.scr.CellAt(x, y) +} + +// SetCell implements [uv.Screen]. +func (c *Canvas) SetCell(x int, y int, cell *uv.Cell) { + c.scr.SetCell(x, y, cell) +} + +// WidthMethod implements [uv.Screen]. +func (c *Canvas) WidthMethod() uv.WidthMethod { + return c.scr.WidthMethod() +} + +// Compose composes a [Layer] or any [uv.Drawable] onto the [Canvas]. +func (c *Canvas) Compose(drawer uv.Drawable) *Canvas { + drawer.Draw(c, c.Bounds()) + return c +} + +// Draw draws the [Canvas] onto the given [uv.Screen] within the specified +// area. +// +// It implements [uv.Drawable]. +func (c *Canvas) Draw(scr uv.Screen, area uv.Rectangle) { + c.scr.Draw(scr, area) +} + +// Render renders the canvas into a styled string. Any trailing spaces are +// trimmed. +func (c *Canvas) Render() string { + return uv.TrimSpace(c.scr.Render()) +} diff --git a/vendor/charm.land/lipgloss/v2/color.go b/vendor/charm.land/lipgloss/v2/color.go new file mode 100644 index 00000000..7443dc10 --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/color.go @@ -0,0 +1,359 @@ +package lipgloss + +import ( + "cmp" + "errors" + "image/color" + "strconv" + "strings" + + "github.com/charmbracelet/colorprofile" + "github.com/charmbracelet/x/ansi" + "github.com/lucasb-eyer/go-colorful" +) + +func clamp[T cmp.Ordered](v, low, high T) T { + if high < low { + high, low = low, high + } + return min(high, max(low, v)) +} + +// 4-bit color constants. +const ( + Black ansi.BasicColor = iota + Red + Green + Yellow + Blue + Magenta + Cyan + White + + BrightBlack + BrightRed + BrightGreen + BrightYellow + BrightBlue + BrightMagenta + BrightCyan + BrightWhite +) + +var noColor = NoColor{} + +// NoColor is used to specify the absence of color styling. When this is active +// foreground colors will be rendered with the terminal's default text color, +// and background colors will not be drawn at all. +// +// Example usage: +// +// var style = someStyle.Background(lipgloss.NoColor{}) +type NoColor struct{} + +// RGBA returns the RGBA value of this color. Because we have to return +// something, despite this color being the absence of color, we're returning +// black with 100% opacity. +// +// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. +func (n NoColor) RGBA() (r, g, b, a uint32) { + return 0x0, 0x0, 0x0, 0xFFFF //nolint:mnd +} + +// Color specifies a color by hex or ANSI256 value. For example: +// +// ansiColor := lipgloss.Color("1") // The same as lipgloss.Red +// ansi256Color := lipgloss.Color("21") +// hexColor := lipgloss.Color("#0000ff") +func Color(s string) color.Color { + if strings.HasPrefix(s, "#") { + c, err := parseHex(s) + if err != nil { + return noColor + } + return c + } + + i, err := strconv.Atoi(s) + if err != nil { + return noColor + } + + if i < 0 { + // Only positive numbers + i = -i + } + + if i < 16 { + return ansi.BasicColor(i) //nolint:gosec + } else if i < 256 { + return ANSIColor(i) //nolint:gosec + } + + r, g, b := uint8((i>>16)&0xff), uint8(i>>8&0xff), uint8(i&0xff) //nolint:gosec + return color.RGBA{R: r, G: g, B: b, A: 0xff} +} + +var errInvalidFormat = errors.New("invalid hex format") // pre-allocated. + +// parseHex parses a hex color string and returns a color.RGBA. The string can be +// in the format #RRGGBB or #RGB. This is a more performant implementation of +// [colorful.Hex]. +func parseHex(s string) (c color.RGBA, err error) { + c.A = 0xff + + if len(s) == 0 || s[0] != '#' { + return c, errInvalidFormat + } + + hexToByte := func(b byte) byte { + switch { + case b >= '0' && b <= '9': + return b - '0' + case b >= 'a' && b <= 'f': + return b - 'a' + 10 + case b >= 'A' && b <= 'F': + return b - 'A' + 10 + } + err = errInvalidFormat + return 0 + } + + switch len(s) { + case 7: + c.R = hexToByte(s[1])<<4 + hexToByte(s[2]) + c.G = hexToByte(s[3])<<4 + hexToByte(s[4]) + c.B = hexToByte(s[5])<<4 + hexToByte(s[6]) + case 4: + c.R = hexToByte(s[1]) * 17 + c.G = hexToByte(s[2]) * 17 + c.B = hexToByte(s[3]) * 17 + default: + err = errInvalidFormat + } + return c, err +} + +// RGBColor is a color specified by red, green, and blue values. +type RGBColor struct { + R uint8 + G uint8 + B uint8 +} + +// RGBA returns the RGBA value of this color. This satisfies the Go Color +// interface. +func (c RGBColor) RGBA() (r, g, b, a uint32) { + const shift = 8 + r |= uint32(c.R) << shift + g |= uint32(c.G) << shift + b |= uint32(c.B) << shift + a = 0xFFFF + return +} + +// ANSIColor is a color specified by an ANSI256 color value. +// +// Example usage: +// +// colorA := lipgloss.ANSIColor(8) +// colorB := lipgloss.ANSIColor(134) +type ANSIColor = ansi.IndexedColor + +// LightDarkFunc is a function that returns a color based on whether the +// terminal has a light or dark background. You can create one of these with +// [LightDark]. +// +// Example: +// +// lightDark := lipgloss.LightDark(hasDarkBackground) +// red, blue := lipgloss.Color("#ff0000"), lipgloss.Color("#0000ff") +// myHotColor := lightDark(red, blue) +// +// For more info see [LightDark]. +type LightDarkFunc func(light, dark color.Color) color.Color + +// LightDark is a simple helper type that can be used to choose the appropriate +// color based on whether the terminal has a light or dark background. +// +// lightDark := lipgloss.LightDark(hasDarkBackground) +// red, blue := lipgloss.Color("#ff0000"), lipgloss.Color("#0000ff") +// myHotColor := lightDark(red, blue) +// +// In practice, there are slightly different workflows between Bubble Tea and +// Lip Gloss standalone. +// +// In Bubble Tea, listen for tea.BackgroundColorMsg, which automatically +// flows through Update on start. This message will be received whenever the +// background color changes: +// +// case tea.BackgroundColorMsg: +// m.hasDarkBackground = msg.IsDark() +// +// Later, when you're rendering use: +// +// lightDark := lipgloss.LightDark(m.hasDarkBackground) +// red, blue := lipgloss.Color("#ff0000"), lipgloss.Color("#0000ff") +// myHotColor := lightDark(red, blue) +// +// In standalone Lip Gloss, the workflow is simpler: +// +// hasDarkBG := lipgloss.HasDarkBackground(os.Stdin, os.Stdout) +// lightDark := lipgloss.LightDark(hasDarkBG) +// red, blue := lipgloss.Color("#ff0000"), lipgloss.Color("#0000ff") +// myHotColor := lightDark(red, blue) +func LightDark(isDark bool) LightDarkFunc { + return func(light, dark color.Color) color.Color { + if isDark { + return dark + } + return light + } +} + +// isDarkColor returns whether the given color is dark (based on the luminance +// portion of the color as interpreted as HSL). +// +// Example usage: +// +// color := lipgloss.Color("#0000ff") +// if lipgloss.isDarkColor(color) { +// fmt.Println("It's dark! I love darkness!") +// } else { +// fmt.Println("It's light! Cover your eyes!") +// } +func isDarkColor(c color.Color) bool { + col, ok := colorful.MakeColor(c) + if !ok { + return true + } + + _, _, l := col.Hsl() + return l < 0.5 //nolint:mnd +} + +// CompleteFunc is a function that returns the appropriate color based on the +// given color profile. +// +// Example usage: +// +// p := colorprofile.Detect(os.Stderr, os.Environ()) +// complete := lipgloss.Complete(p) +// color := complete( +// lipgloss.Color(1), // ANSI +// lipgloss.Color(124), // ANSI256 +// lipgloss.Color("#ff34ac"), // TrueColor +// ) +// fmt.Println("Ooh, pretty color: ", color) +// +// For more info see [Complete]. +type CompleteFunc func(ansi, ansi256, truecolor color.Color) color.Color + +// Complete returns a function that will return the appropriate color based on +// the given color profile. +// +// Example usage: +// +// p := colorprofile.Detect(os.Stderr, os.Environ()) +// complete := lipgloss.Complete(p) +// color := complete( +// lipgloss.Color(1), // ANSI +// lipgloss.Color(124), // ANSI256 +// lipgloss.Color("#ff34ac"), // TrueColor +// ) +// fmt.Println("Ooh, pretty color: ", color) +func Complete(p colorprofile.Profile) CompleteFunc { + return func(ansi, ansi256, truecolor color.Color) color.Color { + switch p { //nolint:exhaustive + case colorprofile.ANSI: + return ansi + case colorprofile.ANSI256: + return ansi256 + case colorprofile.TrueColor: + return truecolor + } + return noColor + } +} + +// ensureNotTransparent ensures that the alpha value of a color is not 0, and if +// it is, we will set it to 1. This is useful for when we are converting from +// RGB -> RGBA, and the alpha value is lost in the conversion for gradient purposes. +func ensureNotTransparent(c color.Color) color.Color { + _, _, _, a := c.RGBA() + if a == 0 { + return Alpha(c, 1) + } + return c +} + +// Alpha adjusts the alpha value of a color using a 0-1 (clamped) float scale +// 0 = transparent, 1 = opaque. +func Alpha(c color.Color, alpha float64) color.Color { + if c == nil { + return nil + } + + r, g, b, _ := c.RGBA() + return color.RGBA{ + R: uint8(min(255, float64(r>>8))), + G: uint8(min(255, float64(g>>8))), + B: uint8(min(255, float64(b>>8))), + A: uint8(clamp(alpha, 0, 1) * 255), + } +} + +// Complementary returns the complementary color (180° away on color wheel) of +// the given color. This is useful for creating a contrasting color. +func Complementary(c color.Color) color.Color { + if c == nil { + return nil + } + + // Offset hue by 180°. + cf, _ := colorful.MakeColor(ensureNotTransparent(c)) + + h, s, v := cf.Hsv() + h += 180 + if h >= 360 { + h -= 360 + } else if h < 0 { + h += 360 + } + + return colorful.Hsv(h, s, v).Clamped() +} + +// Darken takes a color and makes it darker by a specific percentage (0-1, clamped). +func Darken(c color.Color, percent float64) color.Color { + if c == nil { + return nil + } + + mult := 1.0 - clamp(percent, 0, 1) + + r, g, b, a := c.RGBA() + return color.RGBA{ + R: uint8(float64(r>>8) * mult), + G: uint8(float64(g>>8) * mult), + B: uint8(float64(b>>8) * mult), + A: uint8(min(255, float64(a>>8))), + } +} + +// Lighten makes a color lighter by a specific percentage (0-1, clamped). +func Lighten(c color.Color, percent float64) color.Color { + if c == nil { + return nil + } + + add := 255 * (clamp(percent, 0, 1)) + + r, g, b, a := c.RGBA() + return color.RGBA{ + R: uint8(min(255, float64(r>>8)+add)), + G: uint8(min(255, float64(g>>8)+add)), + B: uint8(min(255, float64(b>>8)+add)), + A: uint8(min(255, float64(a>>8))), + } +} diff --git a/vendor/github.com/charmbracelet/lipgloss/get.go b/vendor/charm.land/lipgloss/v2/get.go similarity index 80% rename from vendor/github.com/charmbracelet/lipgloss/get.go rename to vendor/charm.land/lipgloss/v2/get.go index 422b4ce9..d1fe1d66 100644 --- a/vendor/github.com/charmbracelet/lipgloss/get.go +++ b/vendor/charm.land/lipgloss/v2/get.go @@ -1,6 +1,7 @@ package lipgloss import ( + "image/color" "strings" "github.com/charmbracelet/x/ansi" @@ -20,7 +21,19 @@ func (s Style) GetItalic() bool { // GetUnderline returns the style's underline value. If no value is set false is // returned. func (s Style) GetUnderline() bool { - return s.getAsBool(underlineKey, false) + return s.ul != UnderlineNone +} + +// GetUnderlineStyle returns the style's underline style. If no value is set +// UnderlineNone is returned. +func (s Style) GetUnderlineStyle() Underline { + return s.ul +} + +// GetUnderlineColor returns the style's underline color. If no value is set +// NoColor{} is returned. +func (s Style) GetUnderlineColor() color.Color { + return s.getAsColor(underlineColorKey) } // GetStrikethrough returns the style's strikethrough value. If no value is set false @@ -49,13 +62,13 @@ func (s Style) GetFaint() bool { // GetForeground returns the style's foreground color. If no value is set // NoColor{} is returned. -func (s Style) GetForeground() TerminalColor { +func (s Style) GetForeground() color.Color { return s.getAsColor(foregroundKey) } // GetBackground returns the style's background color. If no value is set // NoColor{} is returned. -func (s Style) GetBackground() TerminalColor { +func (s Style) GetBackground() color.Color { return s.getAsColor(backgroundKey) } @@ -134,6 +147,16 @@ func (s Style) GetPaddingLeft() int { return s.getAsInt(paddingLeftKey) } +// GetPaddingChar returns the style's padding character. If no value is set a +// space is returned. +func (s Style) GetPaddingChar() rune { + char := s.getAsRune(paddingCharKey) + if char == 0 { + return ' ' + } + return char +} + // GetHorizontalPadding returns the style's left and right padding. Unset // values are measured as 0. func (s Style) GetHorizontalPadding() int { @@ -185,6 +208,16 @@ func (s Style) GetMarginLeft() int { return s.getAsInt(marginLeftKey) } +// GetMarginChar returns the style's padding character. If no value is set a +// space is returned. +func (s Style) GetMarginChar() rune { + char := s.getAsRune(marginCharKey) + if char == 0 { + return ' ' + } + return char +} + // GetHorizontalMargins returns the style's left and right margins. Unset // values are measured as 0. func (s Style) GetHorizontalMargins() int { @@ -241,49 +274,61 @@ func (s Style) GetBorderLeft() bool { // GetBorderTopForeground returns the style's border top foreground color. If // no value is set NoColor{} is returned. -func (s Style) GetBorderTopForeground() TerminalColor { +func (s Style) GetBorderTopForeground() color.Color { return s.getAsColor(borderTopForegroundKey) } // GetBorderRightForeground returns the style's border right foreground color. // If no value is set NoColor{} is returned. -func (s Style) GetBorderRightForeground() TerminalColor { +func (s Style) GetBorderRightForeground() color.Color { return s.getAsColor(borderRightForegroundKey) } // GetBorderBottomForeground returns the style's border bottom foreground // color. If no value is set NoColor{} is returned. -func (s Style) GetBorderBottomForeground() TerminalColor { +func (s Style) GetBorderBottomForeground() color.Color { return s.getAsColor(borderBottomForegroundKey) } // GetBorderLeftForeground returns the style's border left foreground // color. If no value is set NoColor{} is returned. -func (s Style) GetBorderLeftForeground() TerminalColor { +func (s Style) GetBorderLeftForeground() color.Color { return s.getAsColor(borderLeftForegroundKey) } +// GetBorderForegroundBlend returns the style's border blend foreground +// colors. If no value is set, nil is returned. +func (s Style) GetBorderForegroundBlend() []color.Color { + return s.getAsColors(borderForegroundBlendKey) +} + +// GetBorderForegroundBlendOffset returns the style's border blend offset. If no +// value is set, 0 is returned. +func (s Style) GetBorderForegroundBlendOffset() int { + return s.getAsInt(borderForegroundBlendOffsetKey) +} + // GetBorderTopBackground returns the style's border top background color. If // no value is set NoColor{} is returned. -func (s Style) GetBorderTopBackground() TerminalColor { +func (s Style) GetBorderTopBackground() color.Color { return s.getAsColor(borderTopBackgroundKey) } // GetBorderRightBackground returns the style's border right background color. // If no value is set NoColor{} is returned. -func (s Style) GetBorderRightBackground() TerminalColor { +func (s Style) GetBorderRightBackground() color.Color { return s.getAsColor(borderRightBackgroundKey) } // GetBorderBottomBackground returns the style's border bottom background // color. If no value is set NoColor{} is returned. -func (s Style) GetBorderBottomBackground() TerminalColor { +func (s Style) GetBorderBottomBackground() color.Color { return s.getAsColor(borderBottomBackgroundKey) } // GetBorderLeftBackground returns the style's border left background // color. If no value is set NoColor{} is returned. -func (s Style) GetBorderLeftBackground() TerminalColor { +func (s Style) GetBorderLeftBackground() color.Color { return s.getAsColor(borderLeftBackgroundKey) } @@ -300,7 +345,10 @@ func (s Style) GetBorderTopWidth() int { // runes of varying widths, the widest rune is returned. If no border exists on // the top edge, 0 is returned. func (s Style) GetBorderTopSize() int { - if !s.getAsBool(borderTopKey, false) && !s.implicitBorders() { + if s.isBorderStyleSetWithoutSides() { + return 1 + } + if !s.getAsBool(borderTopKey, false) { return 0 } return s.getBorderStyle().GetTopSize() @@ -310,7 +358,10 @@ func (s Style) GetBorderTopSize() int { // runes of varying widths, the widest rune is returned. If no border exists on // the left edge, 0 is returned. func (s Style) GetBorderLeftSize() int { - if !s.getAsBool(borderLeftKey, false) && !s.implicitBorders() { + if s.isBorderStyleSetWithoutSides() { + return 1 + } + if !s.getAsBool(borderLeftKey, false) { return 0 } return s.getBorderStyle().GetLeftSize() @@ -320,7 +371,10 @@ func (s Style) GetBorderLeftSize() int { // contain runes of varying widths, the widest rune is returned. If no border // exists on the left edge, 0 is returned. func (s Style) GetBorderBottomSize() int { - if !s.getAsBool(borderBottomKey, false) && !s.implicitBorders() { + if s.isBorderStyleSetWithoutSides() { + return 1 + } + if !s.getAsBool(borderBottomKey, false) { return 0 } return s.getBorderStyle().GetBottomSize() @@ -330,7 +384,10 @@ func (s Style) GetBorderBottomSize() int { // contain runes of varying widths, the widest rune is returned. If no border // exists on the right edge, 0 is returned. func (s Style) GetBorderRightSize() int { - if !s.getAsBool(borderRightKey, false) && !s.implicitBorders() { + if s.isBorderStyleSetWithoutSides() { + return 1 + } + if !s.getAsBool(borderRightKey, false) { return 0 } return s.getBorderStyle().GetRightSize() @@ -414,11 +471,36 @@ func (s Style) GetTransform() func(string) string { return s.getAsTransform(transformKey) } +// GetHyperlink returns the hyperlink along with its parameters. If no +// hyperlink is set, empty strings are returned. +func (s Style) GetHyperlink() (link, params string) { + if s.isSet(linkKey) { + link = s.link + } + if s.isSet(linkParamsKey) { + params = s.linkParams + } + return +} + // Returns whether or not the given property is set. func (s Style) isSet(k propKey) bool { return s.props.has(k) } +func (s Style) getAsRune(k propKey) rune { + if !s.isSet(k) { + return 0 + } + switch k { //nolint:exhaustive + case paddingCharKey: + return s.paddingChar + case marginCharKey: + return s.marginChar + } + return 0 +} + func (s Style) getAsBool(k propKey, defaultVal bool) bool { if !s.isSet(k) { return defaultVal @@ -426,12 +508,25 @@ func (s Style) getAsBool(k propKey, defaultVal bool) bool { return s.attrs&int(k) != 0 } -func (s Style) getAsColor(k propKey) TerminalColor { +func (s Style) getAsColors(k propKey) (colors []color.Color) { + if !s.isSet(k) { + return nil + } + + switch k { //nolint:exhaustive + case borderForegroundBlendKey: + return s.borderBlendFgColor + } + + return nil +} + +func (s Style) getAsColor(k propKey) color.Color { if !s.isSet(k) { return noColor } - var c TerminalColor + var c color.Color switch k { //nolint:exhaustive case foregroundKey: c = s.fgColor @@ -455,6 +550,8 @@ func (s Style) getAsColor(k propKey) TerminalColor { c = s.borderBottomBgColor case borderLeftBackgroundKey: c = s.borderLeftBgColor + case underlineColorKey: + c = s.ulColor } if c != nil { @@ -489,6 +586,8 @@ func (s Style) getAsInt(k propKey) int { return s.marginBottom case marginLeftKey: return s.marginLeft + case borderForegroundBlendOffsetKey: + return s.borderForegroundBlendOffset case maxWidthKey: return s.maxWidth case maxHeightKey: @@ -519,20 +618,6 @@ func (s Style) getBorderStyle() Border { return s.borderStyle } -// Returns whether or not the style has implicit borders. This happens when -// a border style has been set but no border sides have been explicitly turned -// on or off. -func (s Style) implicitBorders() bool { - var ( - borderStyle = s.getBorderStyle() - topSet = s.isSet(borderTopKey) - rightSet = s.isSet(borderRightKey) - bottomSet = s.isSet(borderBottomKey) - leftSet = s.isSet(borderLeftKey) - ) - return borderStyle != noBorder && !(topSet || rightSet || bottomSet || leftSet) -} - func (s Style) getAsTransform(propKey) func(string) string { if !s.isSet(transformKey) { return nil @@ -543,6 +628,8 @@ func (s Style) getAsTransform(propKey) func(string) string { // Split a string into lines, additionally returning the size of the widest // line. func getLines(s string) (lines []string, widest int) { + s = strings.ReplaceAll(s, "\t", " ") + s = strings.ReplaceAll(s, "\r\n", "\n") lines = strings.Split(s, "\n") for _, l := range lines { @@ -554,3 +641,17 @@ func getLines(s string) (lines []string, widest int) { return lines, widest } + +// isBorderStyleSetWithoutSides returns true if the border style is set but no +// sides are set. This is used to determine if the border should be rendered by +// default. +func (s Style) isBorderStyleSetWithoutSides() bool { + var ( + border = s.getBorderStyle() + topSet = s.isSet(borderTopKey) + rightSet = s.isSet(borderRightKey) + bottomSet = s.isSet(borderBottomKey) + leftSet = s.isSet(borderLeftKey) + ) + return border != noBorder && !(topSet || rightSet || bottomSet || leftSet) //nolint:staticcheck +} diff --git a/vendor/github.com/charmbracelet/lipgloss/join.go b/vendor/charm.land/lipgloss/v2/join.go similarity index 97% rename from vendor/github.com/charmbracelet/lipgloss/join.go rename to vendor/charm.land/lipgloss/v2/join.go index b0a23a54..349ae552 100644 --- a/vendor/github.com/charmbracelet/lipgloss/join.go +++ b/vendor/charm.land/lipgloss/v2/join.go @@ -60,7 +60,7 @@ func JoinHorizontal(pos Position, strs ...string) string { extraLines := make([]string, maxHeight-len(blocks[i])) - switch pos { //nolint:exhaustive + switch pos { case Top: blocks[i] = append(blocks[i], extraLines...) @@ -139,7 +139,7 @@ func JoinVertical(pos Position, strs ...string) string { for j, line := range block { w := maxWidth - ansi.StringWidth(line) - switch pos { //nolint:exhaustive + switch pos { case Left: b.WriteString(line) b.WriteString(strings.Repeat(" ", w)) @@ -165,7 +165,7 @@ func JoinVertical(pos Position, strs ...string) string { // Write a newline as long as we're not on the last line of the // last block. - if !(i == len(blocks)-1 && j == len(block)-1) { + if !(i == len(blocks)-1 && j == len(block)-1) { //nolint:staticcheck b.WriteRune('\n') } } diff --git a/vendor/charm.land/lipgloss/v2/layer.go b/vendor/charm.land/lipgloss/v2/layer.go new file mode 100644 index 00000000..e30d1ef2 --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/layer.go @@ -0,0 +1,327 @@ +package lipgloss + +import ( + "fmt" + "image" + "slices" + + uv "github.com/charmbracelet/ultraviolet" +) + +// Layer represents a visual layer with content and positioning. It's a pure +// data structure that defines the layer hierarchy without any computation. +type Layer struct { + id string + content string + width, height int + x, y, z int + layers []*Layer +} + +// NewLayer creates a new [Layer] with the given content and optional child layers. +func NewLayer(content string, layers ...*Layer) *Layer { + l := &Layer{ + content: content, + } + l.AddLayers(layers...) + return l +} + +// GetContent returns the content of the Layer. +func (l *Layer) GetContent() string { + return l.content +} + +// Width returns the width of the Layer. +func (l *Layer) Width() int { + return l.width +} + +// Height returns the height of the Layer. +func (l *Layer) Height() int { + return l.height +} + +// GetID returns the ID of the Layer. +func (l *Layer) GetID() string { + return l.id +} + +// ID sets the ID of the Layer. +func (l *Layer) ID(id string) *Layer { + l.id = id + return l +} + +// X sets the x-coordinate of the Layer relative to its parent. +func (l *Layer) X(x int) *Layer { + l.x = x + return l +} + +// Y sets the y-coordinate of the Layer relative to its parent. +func (l *Layer) Y(y int) *Layer { + l.y = y + return l +} + +// Z sets the z-index of the Layer relative to its parent. +func (l *Layer) Z(z int) *Layer { + l.z = z + return l +} + +// GetX returns the x-coordinate of the Layer relative to its parent. +func (l *Layer) GetX() int { + return l.x +} + +// GetY returns the y-coordinate of the Layer relative to its parent. +func (l *Layer) GetY() int { + return l.y +} + +// GetZ returns the z-index of the Layer relative to its parent. +func (l *Layer) GetZ() int { + return l.z +} + +// AddLayers adds child layers to the Layer. +func (l *Layer) AddLayers(layers ...*Layer) *Layer { + for i, layer := range layers { + if layer == nil { + panic(fmt.Sprintf("layer at index %d is nil", i)) + } + l.layers = append(l.layers, layer) + } + area := l.boundsWithOffset(0, 0) + l.width = area.Dx() + l.height = area.Dy() + return l +} + +// GetLayer returns a descendant layer by its ID, or nil if not found. +// Layers with empty IDs are skipped. +func (l *Layer) GetLayer(id string) *Layer { + if id == "" { + return nil + } + if l.id == id { + return l + } + for _, child := range l.layers { + if found := child.GetLayer(id); found != nil { + return found + } + } + return nil +} + +// MaxZ returns the maximum z-index among this layer and all its descendants. +func (l *Layer) MaxZ() int { + maxZ := l.z + for _, child := range l.layers { + childMaxZ := child.MaxZ() + if childMaxZ > maxZ { + maxZ = childMaxZ + } + } + return maxZ +} + +// boundsWithOffset calculates bounds with parent offset applied. +func (l *Layer) boundsWithOffset(parentX, parentY int) image.Rectangle { + absX := l.x + parentX + absY := l.y + parentY + + width, height := Width(l.content), Height(l.content) + bounds := image.Rectangle{ + Min: image.Pt(absX, absY), + Max: image.Pt(absX+width, absY+height), + } + + for _, child := range l.layers { + bounds = bounds.Union(child.boundsWithOffset(absX, absY)) + } + + return bounds +} + +var _ uv.Drawable = (*Layer)(nil) + +// Draw draws the content of the layer on the screen at the specified area. +func (l *Layer) Draw(scr uv.Screen, area uv.Rectangle) { + content := uv.NewStyledString(l.content) + content.Draw(scr, area) +} + +// LayerHit represents the result of a hit test on a [Layer]. +type LayerHit struct { + id string + layer *Layer + bounds image.Rectangle +} + +// Empty returns true if the LayerHit represents no hit. +func (lh LayerHit) Empty() bool { + return lh.layer == nil +} + +// ID returns the ID of the hit Layer. +func (lh LayerHit) ID() string { + return lh.id +} + +// Layer returns the layer that was hit. +func (lh LayerHit) Layer() *Layer { + return lh.layer +} + +// Bounds returns the bounds of the LayerHit. +func (lh LayerHit) Bounds() image.Rectangle { + return lh.bounds +} + +// Compositor manages the composition of layers. It flattens a layer hierarchy +// once and provides efficient drawing and hit testing operations. All computation +// related to layers happens in the Compositor. +type Compositor struct { + root *Layer + layers []compositeLayer + index map[string]*Layer + bounds image.Rectangle +} + +// compositeLayer holds a flattened layer with its calculated absolute position and bounds. +type compositeLayer struct { + layer *Layer + absX int + absY int + bounds image.Rectangle +} + +// NewCompositor creates a new Compositor with an internal root layer. Optional +// layers can be provided which will be added as children of the root. The layer +// hierarchy is flattened and sorted by z-index for efficient rendering and hit testing. +func NewCompositor(layers ...*Layer) *Compositor { + root := NewLayer("") + root.AddLayers(layers...) + c := &Compositor{ + root: root, + index: make(map[string]*Layer), + } + c.flatten() + return c +} + +// AddLayers adds layers to the compositor's root and refreshes the internal state. +func (c *Compositor) AddLayers(layers ...*Layer) *Compositor { + c.root.AddLayers(layers...) + c.flatten() + return c +} + +// flatten builds the internal flattened layer list and calculates overall bounds. +func (c *Compositor) flatten() { + c.layers = nil + c.index = make(map[string]*Layer) + c.flattenRecursive(c.root, 0, 0) + + // Sort by absolute z-index (lowest to highest for drawing) + slices.SortFunc(c.layers, func(a, b compositeLayer) int { + return a.layer.z - b.layer.z + }) + + // Calculate overall bounds + if len(c.layers) > 0 { + c.bounds = c.layers[0].bounds + for i := 1; i < len(c.layers); i++ { + c.bounds = c.bounds.Union(c.layers[i].bounds) + } + } +} + +// flattenRecursive recursively collects all layers with their absolute positions. +func (c *Compositor) flattenRecursive(layer *Layer, parentX, parentY int) { + absX := layer.x + parentX + absY := layer.y + parentY + + width, height := Width(layer.content), Height(layer.content) + bounds := image.Rectangle{ + Min: image.Pt(absX, absY), + Max: image.Pt(absX+width, absY+height), + } + + c.layers = append(c.layers, compositeLayer{ + layer: layer, + absX: absX, + absY: absY, + bounds: bounds, + }) + + // Index layer by ID if it has one + if layer.id != "" { + c.index[layer.id] = layer + } + + for _, child := range layer.layers { + c.flattenRecursive(child, absX, absY) + } +} + +// Bounds returns the overall bounds of all layers in the compositor. +func (c *Compositor) Bounds() image.Rectangle { + return c.bounds +} + +// Draw draws all layers onto the given [uv.Screen] in z-index order. +func (c *Compositor) Draw(scr uv.Screen, area image.Rectangle) { + for _, cl := range c.layers { + if cl.bounds.Overlaps(area) { + cl.layer.Draw(scr, cl.bounds) + } + } +} + +// Hit performs a hit test at the given (x, y) coordinates. If a layer is hit, +// it returns the ID of the top-most layer at that point. Layers with empty IDs +// are ignored. If no layer is hit, it returns an empty [LayerHit]. +func (c *Compositor) Hit(x, y int) LayerHit { + var hit LayerHit + pt := image.Pt(x, y) + // Check from highest z to lowest (reverse order) + for i := len(c.layers) - 1; i >= 0; i-- { + cl := c.layers[i] + if cl.layer.id != "" && pt.In(cl.bounds) { + hit.id = cl.layer.id + hit.layer = cl.layer + hit.bounds = cl.bounds + return hit + } + } + return hit +} + +// GetLayer returns a layer by its ID, or nil if not found. +// Layers with empty IDs are not indexed and cannot be retrieved. +func (c *Compositor) GetLayer(id string) *Layer { + if id == "" { + return nil + } + return c.index[id] +} + +// Refresh re-flattens the layer hierarchy. Call this after modifying the layer +// tree structure or positions to update the compositor's internal state. +func (c *Compositor) Refresh() { + c.flatten() +} + +// Render renders the compositor into a styled string. This is a helper +// function that creates a temporary canvas, draws the compositor onto it, and +// returns the resulting string. +func (c *Compositor) Render() string { + width, height := c.bounds.Dx(), c.bounds.Dy() + canvas := NewCanvas(width, height) + return canvas.Compose(c).Render() +} diff --git a/vendor/charm.land/lipgloss/v2/lipgloss.go b/vendor/charm.land/lipgloss/v2/lipgloss.go new file mode 100644 index 00000000..3fb4e6f2 --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/lipgloss.go @@ -0,0 +1,3 @@ +// Package lipgloss provides style definitions for nice terminal layouts. Built +// with TUIs in mind. +package lipgloss diff --git a/vendor/github.com/charmbracelet/lipgloss/position.go b/vendor/charm.land/lipgloss/v2/position.go similarity index 69% rename from vendor/github.com/charmbracelet/lipgloss/position.go rename to vendor/charm.land/lipgloss/v2/position.go index 185f5af3..ce91442d 100644 --- a/vendor/github.com/charmbracelet/lipgloss/position.go +++ b/vendor/charm.land/lipgloss/v2/position.go @@ -34,26 +34,13 @@ const ( // Place places a string or text block vertically in an unstyled box of a given // width or height. func Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string { - return renderer.Place(width, height, hPos, vPos, str, opts...) -} - -// Place places a string or text block vertically in an unstyled box of a given -// width or height. -func (r *Renderer) Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string { - return r.PlaceVertical(height, vPos, r.PlaceHorizontal(width, hPos, str, opts...), opts...) -} - -// PlaceHorizontal places a string or text block horizontally in an unstyled -// block of a given width. If the given width is shorter than the max width of -// the string (measured by its longest line) this will be a noop. -func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string { - return renderer.PlaceHorizontal(width, pos, str, opts...) + return PlaceVertical(height, vPos, PlaceHorizontal(width, hPos, str, opts...), opts...) } // PlaceHorizontal places a string or text block horizontally in an unstyled // block of a given width. If the given width is shorter than the max width of // the string (measured by its longest line) this will be a noöp. -func (r *Renderer) PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string { +func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string { lines, contentWidth := getLines(str) gap := width - contentWidth @@ -61,14 +48,14 @@ func (r *Renderer) PlaceHorizontal(width int, pos Position, str string, opts ... return str } - ws := newWhitespace(r, opts...) + ws := newWhitespace(opts...) var b strings.Builder for i, l := range lines { // Is this line shorter than the longest line? short := max(0, contentWidth-ansi.StringWidth(l)) - switch pos { //nolint:exhaustive + switch pos { case Left: b.WriteString(l) b.WriteString(ws.render(gap + short)) @@ -97,17 +84,10 @@ func (r *Renderer) PlaceHorizontal(width int, pos Position, str string, opts ... return b.String() } -// PlaceVertical places a string or text block vertically in an unstyled block -// of a given height. If the given height is shorter than the height of the -// string (measured by its newlines) then this will be a noop. -func PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string { - return renderer.PlaceVertical(height, pos, str, opts...) -} - // PlaceVertical places a string or text block vertically in an unstyled block // of a given height. If the given height is shorter than the height of the // string (measured by its newlines) then this will be a noöp. -func (r *Renderer) PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string { +func PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string { contentHeight := strings.Count(str, "\n") + 1 gap := height - contentHeight @@ -115,17 +95,17 @@ func (r *Renderer) PlaceVertical(height int, pos Position, str string, opts ...W return str } - ws := newWhitespace(r, opts...) + ws := newWhitespace(opts...) _, width := getLines(str) emptyLine := ws.render(width) b := strings.Builder{} - switch pos { //nolint:exhaustive + switch pos { case Top: b.WriteString(str) b.WriteRune('\n') - for i := 0; i < gap; i++ { + for i := range gap { b.WriteString(emptyLine) if i < gap-1 { b.WriteRune('\n') @@ -144,7 +124,7 @@ func (r *Renderer) PlaceVertical(height int, pos Position, str string, opts ...W b.WriteString(strings.Repeat(emptyLine+"\n", top)) b.WriteString(str) - for i := 0; i < bottom; i++ { + for range bottom { b.WriteRune('\n') b.WriteString(emptyLine) } diff --git a/vendor/charm.land/lipgloss/v2/query.go b/vendor/charm.land/lipgloss/v2/query.go new file mode 100644 index 00000000..96cd5b94 --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/query.go @@ -0,0 +1,92 @@ +package lipgloss + +import ( + "fmt" + "image/color" + "os" + "runtime" + + "github.com/charmbracelet/x/term" +) + +func backgroundColor(in term.File, out term.File) (color.Color, error) { + state, err := term.MakeRaw(in.Fd()) + if err != nil { + return nil, fmt.Errorf("error setting raw state to detect background color: %w", err) + } + + defer term.Restore(in.Fd(), state) //nolint:errcheck + + bg, err := queryBackgroundColor(in, out) + if err != nil { + return nil, err + } + + return bg, nil +} + +// BackgroundColor queries the terminal's background color. Typically, you'll +// want to query against stdin and either stdout or stderr, depending on what +// you're writing to. +// +// This function is intended for standalone Lip Gloss use only. If you're using +// Bubble Tea, listen for tea.BackgroundColorMsg in your update function. +func BackgroundColor(in term.File, out term.File) (bg color.Color, err error) { + if runtime.GOOS == "windows" { //nolint:nestif + // On Windows, when the input/output is redirected or piped, we need to + // open the console explicitly. + // See https://learn.microsoft.com/en-us/windows/console/getstdhandle#remarks + if !term.IsTerminal(in.Fd()) { + f, err := os.OpenFile("CONIN$", os.O_RDWR, 0o644) //nolint:gosec + if err != nil { + return nil, fmt.Errorf("error opening CONIN$: %w", err) + } + in = f + } + if !term.IsTerminal(out.Fd()) { + f, err := os.OpenFile("CONOUT$", os.O_RDWR, 0o644) //nolint:gosec + if err != nil { + return nil, fmt.Errorf("error opening CONOUT$: %w", err) + } + out = f + } + return backgroundColor(in, out) + } + + // NOTE: On Unix, one of the given files must be a tty. + if !term.IsTerminal(in.Fd()) || !term.IsTerminal(out.Fd()) { + return nil, fmt.Errorf("input/output is not a terminal") + } + for _, f := range []term.File{in, out} { + if bg, err = backgroundColor(f, f); err == nil { + return bg, nil + } + } + + return bg, err +} + +// HasDarkBackground detects whether the terminal has a light or dark +// background. +// +// Typically, you'll want to query against stdin and either stdout or stderr +// depending on what you're writing to. +// +// hasDarkBG := HasDarkBackground(os.Stdin, os.Stdout) +// lightDark := LightDark(hasDarkBG) +// myHotColor := lightDark("#ff0000", "#0000ff") +// +// This is intended for use in standalone Lip Gloss only. In Bubble Tea, listen +// for tea.BackgroundColorMsg in your Update function. +// +// case tea.BackgroundColorMsg: +// hasDarkBackground = msg.IsDark() +// +// By default, this function will return true if it encounters an error. +func HasDarkBackground(in term.File, out term.File) bool { + bg, err := BackgroundColor(in, out) + if err != nil || bg == nil { + return true + } + return isDarkColor(bg) +} diff --git a/vendor/github.com/charmbracelet/lipgloss/ranges.go b/vendor/charm.land/lipgloss/v2/ranges.go similarity index 75% rename from vendor/github.com/charmbracelet/lipgloss/ranges.go rename to vendor/charm.land/lipgloss/v2/ranges.go index d1716998..bb209f0e 100644 --- a/vendor/github.com/charmbracelet/lipgloss/ranges.go +++ b/vendor/charm.land/lipgloss/v2/ranges.go @@ -6,9 +6,8 @@ import ( "github.com/charmbracelet/x/ansi" ) -// StyleRanges allows to, given a string, style ranges of it differently. -// The function will take into account existing styles. -// Ranges should not overlap. +// StyleRanges applying styling to ranges in a string. Existing styles will be +// taken into account. Ranges should not overlap. func StyleRanges(s string, ranges ...Range) string { if len(ranges) == 0 { return s @@ -36,12 +35,13 @@ func StyleRanges(s string, ranges ...Range) string { return buf.String() } -// NewRange returns a range that can be used with [StyleRanges]. +// NewRange returns a range and style that can be used with [StyleRanges]. func NewRange(start, end int, style Style) Range { return Range{start, end, style} } -// Range to be used with [StyleRanges]. +// Range is a range of text and associated styling to be used with +// [StyleRanges]. type Range struct { Start, End int Style Style diff --git a/vendor/github.com/charmbracelet/lipgloss/runes.go b/vendor/charm.land/lipgloss/v2/runes.go similarity index 100% rename from vendor/github.com/charmbracelet/lipgloss/runes.go rename to vendor/charm.land/lipgloss/v2/runes.go diff --git a/vendor/github.com/charmbracelet/lipgloss/set.go b/vendor/charm.land/lipgloss/v2/set.go similarity index 78% rename from vendor/github.com/charmbracelet/lipgloss/set.go rename to vendor/charm.land/lipgloss/v2/set.go index fde38fae..f10b4538 100644 --- a/vendor/github.com/charmbracelet/lipgloss/set.go +++ b/vendor/charm.land/lipgloss/v2/set.go @@ -1,16 +1,25 @@ package lipgloss +import ( + "image/color" + "strings" +) + // Set a value on the underlying rules map. -func (s *Style) set(key propKey, value interface{}) { +func (s *Style) set(key propKey, value any) { // We don't allow negative integers on any of our other values, so just keep // them at zero or above. We could use uints instead, but the // conversions are a little tedious, so we're sticking with ints for // sake of usability. - switch key { //nolint:exhaustive + switch key { case foregroundKey: s.fgColor = colorOrNil(value) case backgroundKey: s.bgColor = colorOrNil(value) + case underlineColorKey: + s.ulColor = colorOrNil(value) + case underlineKey: + s.ul = value.(Underline) case widthKey: s.width = max(0, value.(int)) case heightKey: @@ -27,6 +36,8 @@ func (s *Style) set(key propKey, value interface{}) { s.paddingBottom = max(0, value.(int)) case paddingLeftKey: s.paddingLeft = max(0, value.(int)) + case paddingCharKey: + s.paddingChar = value.(rune) case marginTopKey: s.marginTop = max(0, value.(int)) case marginRightKey: @@ -37,6 +48,8 @@ func (s *Style) set(key propKey, value interface{}) { s.marginLeft = max(0, value.(int)) case marginBackgroundKey: s.marginBgColor = colorOrNil(value) + case marginCharKey: + s.marginChar = value.(rune) case borderStyleKey: s.borderStyle = value.(Border) case borderTopForegroundKey: @@ -47,6 +60,10 @@ func (s *Style) set(key propKey, value interface{}) { s.borderBottomFgColor = colorOrNil(value) case borderLeftForegroundKey: s.borderLeftFgColor = colorOrNil(value) + case borderForegroundBlendKey: + s.borderBlendFgColor = value.([]color.Color) + case borderForegroundBlendOffsetKey: + s.borderForegroundBlendOffset = value.(int) case borderTopBackgroundKey: s.borderTopBgColor = colorOrNil(value) case borderRightBackgroundKey: @@ -65,6 +82,10 @@ func (s *Style) set(key propKey, value interface{}) { s.tabWidth = value.(int) case transformKey: s.transform = value.(func(string) string) + case linkKey: + s.link = value.(string) + case linkParamsKey: + s.linkParams = value.(string) default: if v, ok := value.(bool); ok { //nolint:nestif if v { @@ -88,11 +109,15 @@ func (s *Style) set(key propKey, value interface{}) { // setFrom sets the property from another style. func (s *Style) setFrom(key propKey, i Style) { - switch key { //nolint:exhaustive + switch key { case foregroundKey: s.set(foregroundKey, i.fgColor) case backgroundKey: s.set(backgroundKey, i.bgColor) + case underlineColorKey: + s.set(underlineColorKey, i.ulColor) + case underlineKey: + s.set(underlineKey, i.ul) case widthKey: s.set(widthKey, i.width) case heightKey: @@ -109,6 +134,8 @@ func (s *Style) setFrom(key propKey, i Style) { s.set(paddingBottomKey, i.paddingBottom) case paddingLeftKey: s.set(paddingLeftKey, i.paddingLeft) + case paddingCharKey: + s.set(paddingCharKey, i.paddingChar) case marginTopKey: s.set(marginTopKey, i.marginTop) case marginRightKey: @@ -119,6 +146,8 @@ func (s *Style) setFrom(key propKey, i Style) { s.set(marginLeftKey, i.marginLeft) case marginBackgroundKey: s.set(marginBackgroundKey, i.marginBgColor) + case marginCharKey: + s.set(marginCharKey, i.marginChar) case borderStyleKey: s.set(borderStyleKey, i.borderStyle) case borderTopForegroundKey: @@ -129,6 +158,10 @@ func (s *Style) setFrom(key propKey, i Style) { s.set(borderBottomForegroundKey, i.borderBottomFgColor) case borderLeftForegroundKey: s.set(borderLeftForegroundKey, i.borderLeftFgColor) + case borderForegroundBlendKey: + s.set(borderForegroundBlendKey, i.borderBlendFgColor) + case borderForegroundBlendOffsetKey: + s.set(borderForegroundBlendOffsetKey, i.borderForegroundBlendOffset) case borderTopBackgroundKey: s.set(borderTopBackgroundKey, i.borderTopBgColor) case borderRightBackgroundKey: @@ -151,8 +184,8 @@ func (s *Style) setFrom(key propKey, i Style) { } } -func colorOrNil(c interface{}) TerminalColor { - if c, ok := c.(TerminalColor); ok { +func colorOrNil(c any) color.Color { + if c, ok := c.(color.Color); ok { return c } return nil @@ -173,9 +206,33 @@ func (s Style) Italic(v bool) Style { // Underline sets an underline rule. By default, underlines will not be drawn on // whitespace like margins and padding. To change this behavior set -// UnderlineSpaces. +// [Style.UnderlineSpaces]. func (s Style) Underline(v bool) Style { - s.set(underlineKey, v) + if v { + return s.UnderlineStyle(UnderlineSingle) + } + return s.UnderlineStyle(UnderlineNone) +} + +// UnderlineStyle sets the underline style. This can be used to set the underline +// to be a single, double, curly, dotted, or dashed line. +// +// Note that not all terminal emulators support underline styles. If a style is +// not supported, it will typically fall back to a single underline but this is +// not guaranteed. This depends on the terminal emulator being used. +func (s Style) UnderlineStyle(u Underline) Style { + s.set(underlineKey, u) + return s +} + +// UnderlineColor sets the color of the underline. By default, the underline +// will be the same color as the foreground. +// +// Note that not all terminal emulators support colored underlines. If color is +// not supported, it might produce unexpected results. This depends on the +// terminal emulator being used. +func (s Style) UnderlineColor(c color.Color) Style { + s.set(underlineColorKey, c) return s } @@ -212,19 +269,20 @@ func (s Style) Faint(v bool) Style { // // // Removes the foreground color // s.Foreground(lipgloss.NoColor) -func (s Style) Foreground(c TerminalColor) Style { +func (s Style) Foreground(c color.Color) Style { s.set(foregroundKey, c) return s } // Background sets a background color. -func (s Style) Background(c TerminalColor) Style { +func (s Style) Background(c color.Color) Style { s.set(backgroundKey, c) return s } -// Width sets the width of the block before applying margins. The width, if -// set, also determines where text will wrap. +// Width sets the width of the block before applying margins. This means your +// styled content will exactly equal the size set here. Text will wrap based on +// Padding and Borders set on the style. func (s Style) Width(i int) Style { s.set(widthKey, i) return s @@ -317,6 +375,18 @@ func (s Style) PaddingBottom(i int) Style { return s } +// PaddingChar sets the character used for padding. This is useful for +// rendering blocks with a specific character, such as a space or a dot. +// Example of using [NBSP] as padding to prevent line breaks: +// +// ```go +// s := lipgloss.NewStyle().PaddingChar(lipgloss.NBSP) +// ``` +func (s Style) PaddingChar(r rune) Style { + s.set(paddingCharKey, r) + return s +} + // ColorWhitespace determines whether or not the background color should be // applied to the padding. This is true by default as it's more than likely the // desired and expected behavior, but it can be disabled for certain graphic @@ -382,11 +452,18 @@ func (s Style) MarginBottom(i int) Style { // MarginBackground sets the background color of the margin. Note that this is // also set when inheriting from a style with a background color. In that case // the background color on that style will set the margin color on this style. -func (s Style) MarginBackground(c TerminalColor) Style { +func (s Style) MarginBackground(c color.Color) Style { s.set(marginBackgroundKey, c) return s } +// MarginChar sets the character used for the margin. This is useful for +// rendering blocks with a specific character, such as a space or a dot. +func (s Style) MarginChar(r rune) Style { + s.set(marginCharKey, r) + return s +} + // Border is shorthand for setting the border style and which sides should // have a border at once. The variadic argument sides works as follows: // @@ -487,7 +564,7 @@ func (s Style) BorderLeft(v bool) Style { // top side, followed by the right side, then the bottom, and finally the left. // // With more than four arguments nothing will be set. -func (s Style) BorderForeground(c ...TerminalColor) Style { +func (s Style) BorderForeground(c ...color.Color) Style { if len(c) == 0 { return s } @@ -506,32 +583,80 @@ func (s Style) BorderForeground(c ...TerminalColor) Style { } // BorderTopForeground set the foreground color for the top of the border. -func (s Style) BorderTopForeground(c TerminalColor) Style { +func (s Style) BorderTopForeground(c color.Color) Style { s.set(borderTopForegroundKey, c) return s } // BorderRightForeground sets the foreground color for the right side of the // border. -func (s Style) BorderRightForeground(c TerminalColor) Style { +func (s Style) BorderRightForeground(c color.Color) Style { s.set(borderRightForegroundKey, c) return s } // BorderBottomForeground sets the foreground color for the bottom of the // border. -func (s Style) BorderBottomForeground(c TerminalColor) Style { +func (s Style) BorderBottomForeground(c color.Color) Style { s.set(borderBottomForegroundKey, c) return s } // BorderLeftForeground sets the foreground color for the left side of the // border. -func (s Style) BorderLeftForeground(c TerminalColor) Style { +func (s Style) BorderLeftForeground(c color.Color) Style { s.set(borderLeftForegroundKey, c) return s } +// BorderForegroundBlend sets the foreground colors for the border blend. At least +// 2 colors are required to use blending, otherwise this will no-op with 0 colors, +// and pass to BorderForeground with 1 color. This will override all other border +// foreground colors when used. +// +// When providing colors, in most cases (e.g. when all border sides are enabled), +// you will want to provide a wrapping-set of colors, so the start and end color +// are either the same, or very similar. For example: +// +// lipgloss.NewStyle().BorderForegroundBlend( +// lipgloss.Color("#00FA68"), +// lipgloss.Color("#9900FF"), +// lipgloss.Color("#ED5353"), +// lipgloss.Color("#9900FF"), +// lipgloss.Color("#00FA68"), +// ) +func (s Style) BorderForegroundBlend(c ...color.Color) Style { + if len(c) == 0 { + return s + } + + // Insufficient colors to use blending, pass to BorderForeground. + if len(c) == 1 { + return s.BorderForeground(c...) + } + + s.set(borderForegroundBlendKey, c) + return s +} + +// BorderForegroundBlendOffset sets the border blend offset cells, starting from +// the top left corner. Value can be positive or negative, and does not need to +// equal the dimensions of the border region. Direction (when positive) is as +// follows ("o" is starting point): +// +// o --------> +// ┌──────────┐ +// ^ │ │ | +// | │ │ | +// | │ │ | +// | │ │ v +// └──────────┘ +// <--------- +func (s Style) BorderForegroundBlendOffset(v int) Style { + s.set(borderForegroundBlendOffsetKey, v) + return s +} + // BorderBackground is a shorthand function for setting all of the // background colors of the borders at once. The arguments work as follows: // @@ -547,7 +672,7 @@ func (s Style) BorderLeftForeground(c TerminalColor) Style { // top side, followed by the right side, then the bottom, and finally the left. // // With more than four arguments nothing will be set. -func (s Style) BorderBackground(c ...TerminalColor) Style { +func (s Style) BorderBackground(c ...color.Color) Style { if len(c) == 0 { return s } @@ -566,27 +691,27 @@ func (s Style) BorderBackground(c ...TerminalColor) Style { } // BorderTopBackground sets the background color of the top of the border. -func (s Style) BorderTopBackground(c TerminalColor) Style { +func (s Style) BorderTopBackground(c color.Color) Style { s.set(borderTopBackgroundKey, c) return s } // BorderRightBackground sets the background color of right side the border. -func (s Style) BorderRightBackground(c TerminalColor) Style { +func (s Style) BorderRightBackground(c color.Color) Style { s.set(borderRightBackgroundKey, c) return s } // BorderBottomBackground sets the background color of the bottom of the // border. -func (s Style) BorderBottomBackground(c TerminalColor) Style { +func (s Style) BorderBottomBackground(c color.Color) Style { s.set(borderBottomBackgroundKey, c) return s } // BorderLeftBackground set the background color of the left side of the // border. -func (s Style) BorderLeftBackground(c TerminalColor) Style { +func (s Style) BorderLeftBackground(c color.Color) Style { s.set(borderLeftBackgroundKey, c) return s } @@ -685,10 +810,18 @@ func (s Style) Transform(fn func(string) string) Style { return s } -// Renderer sets the renderer for the style. This is useful for changing the -// renderer for a style that is being used in a different context. -func (s Style) Renderer(r *Renderer) Style { - s.r = r +// Hyperlink sets a hyperlink on a style. This is useful for rendering text that +// can be clicked on in a terminal emulator that supports hyperlinks. +// +// Example: +// +// s := lipgloss.NewStyle().Hyperlink("https://charm.sh") +// s := lipgloss.NewStyle().Hyperlink("https://charm.sh", "id=1") +func (s Style) Hyperlink(link string, params ...string) Style { + s.set(linkKey, link) + if len(params) > 0 { + s.set(linkParamsKey, strings.Join(params, ":")) + } return s } @@ -768,7 +901,7 @@ func whichSidesBool(i ...bool) (top, right, bottom, left bool, ok bool) { // whichSidesColor is like whichSides, except it operates on a series of // boolean values. See the comment on whichSidesInt for details on how this // works. -func whichSidesColor(i ...TerminalColor) (top, right, bottom, left TerminalColor, ok bool) { +func whichSidesColor(i ...color.Color) (top, right, bottom, left color.Color, ok bool) { switch len(i) { case 1: top = i[0] diff --git a/vendor/github.com/charmbracelet/lipgloss/size.go b/vendor/charm.land/lipgloss/v2/size.go similarity index 75% rename from vendor/github.com/charmbracelet/lipgloss/size.go rename to vendor/charm.land/lipgloss/v2/size.go index e169ff5e..e0384d03 100644 --- a/vendor/github.com/charmbracelet/lipgloss/size.go +++ b/vendor/charm.land/lipgloss/v2/size.go @@ -10,10 +10,10 @@ import ( // ignored and characters wider than one cell (such as Chinese characters and // emojis) are appropriately measured. // -// You should use this instead of len(string) len([]rune(string) as neither +// You should use this instead of len(string) or len([]rune(string) as neither // will give you accurate results. func Width(str string) (width int) { - for _, l := range strings.Split(str, "\n") { + for l := range strings.SplitSeq(str, "\n") { w := ansi.StringWidth(l) if w > width { width = w @@ -24,9 +24,8 @@ func Width(str string) (width int) { } // Height returns height of a string in cells. This is done simply by -// counting \n characters. If your strings use \r\n for newlines you should -// convert them to \n first, or simply write a separate function for measuring -// height. +// counting \n characters. If your output has \r\n, that sequence will be +// replaced with a \n in [Style.Render]. func Height(str string) int { return strings.Count(str, "\n") + 1 } diff --git a/vendor/github.com/charmbracelet/lipgloss/style.go b/vendor/charm.land/lipgloss/v2/style.go similarity index 68% rename from vendor/github.com/charmbracelet/lipgloss/style.go rename to vendor/charm.land/lipgloss/v2/style.go index 59fa3ab2..3cd65952 100644 --- a/vendor/github.com/charmbracelet/lipgloss/style.go +++ b/vendor/charm.land/lipgloss/v2/style.go @@ -1,15 +1,18 @@ package lipgloss import ( + "image/color" "strings" "unicode" "github.com/charmbracelet/x/ansi" - "github.com/charmbracelet/x/cellbuf" - "github.com/muesli/termenv" ) -const tabWidthDefault = 4 +const ( + // NBSP is the non-breaking space rune. + NBSP = '\u00A0' + tabWidthDefault = 4 +) // Property for a key. type propKey int64 @@ -19,7 +22,6 @@ const ( // Boolean props come first. boldKey propKey = 1 << iota italicKey - underlineKey strikethroughKey reverseKey blinkKey @@ -29,8 +31,10 @@ const ( colorWhitespaceKey // Non-boolean props. + underlineKey foregroundKey backgroundKey + underlineColorKey widthKey heightKey alignHorizontalKey @@ -41,6 +45,7 @@ const ( paddingRightKey paddingBottomKey paddingLeftKey + paddingCharKey // Margins. marginTopKey @@ -48,6 +53,7 @@ const ( marginBottomKey marginLeftKey marginBackgroundKey + marginCharKey // Border runes. borderStyleKey @@ -63,6 +69,8 @@ const ( borderRightForegroundKey borderBottomForegroundKey borderLeftForegroundKey + borderForegroundBlendKey + borderForegroundBlendOffsetKey // Border background colors. borderTopBackgroundKey @@ -76,6 +84,10 @@ const ( tabWidthKey transformKey + + // Hyperlink. + linkKey + linkParamsKey ) // props is a set of properties. @@ -96,35 +108,53 @@ func (p props) has(k propKey) bool { return p&props(k) != 0 } -// NewStyle returns a new, empty Style. While it's syntactic sugar for the -// Style{} primitive, it's recommended to use this function for creating styles -// in case the underlying implementation changes. It takes an optional string -// value to be set as the underlying string value for this style. -func NewStyle() Style { - return renderer.NewStyle() -} +// Underline is the style of the underline. +// +// Caveats: +// - Not all terminals support all underline styles. +// - Some terminals may render unsupported styles as standard underlines. +// - Terminal themes may affect the visibility of different underline styles. +type Underline = ansi.Underline + +const ( + // UnderlineNone is no underline. + UnderlineNone = ansi.UnderlineNone + // UnderlineSingle is a single underline. This is the default when underline is enabled. + UnderlineSingle = ansi.UnderlineSingle + // UnderlineDouble is a double underline. + UnderlineDouble = ansi.UnderlineDouble + // UnderlineCurly is a curly underline. + UnderlineCurly = ansi.UnderlineCurly + // UnderlineDotted is a dotted underline. + UnderlineDotted = ansi.UnderlineDotted + // UnderlineDashed is a dashed underline. + UnderlineDashed = ansi.UnderlineDashed +) // NewStyle returns a new, empty Style. While it's syntactic sugar for the -// Style{} primitive, it's recommended to use this function for creating styles -// in case the underlying implementation changes. It takes an optional string -// value to be set as the underlying string value for this style. -func (r *Renderer) NewStyle() Style { - s := Style{r: r} - return s +// [Style]{} primitive, it's recommended to use this function for creating styles +// in case the underlying implementation changes. +func NewStyle() Style { + return Style{} } // Style contains a set of rules that comprise a style as a whole. type Style struct { - r *Renderer props props value string + // hyperlink + link, linkParams string + // we store bool props values here attrs int // props that have values - fgColor TerminalColor - bgColor TerminalColor + fgColor color.Color + bgColor color.Color + ulColor color.Color + + ul Underline width int height int @@ -136,22 +166,26 @@ type Style struct { paddingRight int paddingBottom int paddingLeft int + paddingChar rune marginTop int marginRight int marginBottom int marginLeft int - marginBgColor TerminalColor - - borderStyle Border - borderTopFgColor TerminalColor - borderRightFgColor TerminalColor - borderBottomFgColor TerminalColor - borderLeftFgColor TerminalColor - borderTopBgColor TerminalColor - borderRightBgColor TerminalColor - borderBottomBgColor TerminalColor - borderLeftBgColor TerminalColor + marginBgColor color.Color + marginChar rune + + borderStyle Border + borderTopFgColor color.Color + borderRightFgColor color.Color + borderBottomFgColor color.Color + borderLeftFgColor color.Color + borderBlendFgColor []color.Color + borderForegroundBlendOffset int + borderTopBgColor color.Color + borderRightBgColor color.Color + borderBottomBgColor color.Color + borderLeftBgColor color.Color maxWidth int maxHeight int @@ -167,10 +201,10 @@ func joinString(strs ...string) string { } // SetString sets the underlying string value for this style. To render once -// the underlying string is set, use the Style.String. This method is +// the underlying string is set, use the [Style.String]. This method is // a convenience for cases when having a stringer implementation is handy, such // as when using fmt.Sprintf. You can also simply define a style and render out -// strings directly with Style.Render. +// strings directly with [Style.Render]. func (s Style) SetString(strs ...string) Style { s.value = joinString(strs...) return s @@ -232,9 +266,6 @@ func (s Style) Inherit(i Style) Style { // Render applies the defined style formatting to a given string. func (s Style) Render(strs ...string) string { - if s.r == nil { - s.r = renderer - } if s.value != "" { strs = append([]string{s.value}, strs...) } @@ -242,14 +273,12 @@ func (s Style) Render(strs ...string) string { var ( str = joinString(strs...) - p = s.r.ColorProfile() - te = p.String() - teSpace = p.String() - teWhitespace = p.String() + te ansi.Style + teSpace ansi.Style + teWhitespace ansi.Style bold = s.getAsBool(boldKey, false) italic = s.getAsBool(italicKey, false) - underline = s.getAsBool(underlineKey, false) strikethrough = s.getAsBool(strikethroughKey, false) reverse = s.getAsBool(reverseKey, false) blink = s.getAsBool(blinkKey, false) @@ -257,7 +286,9 @@ func (s Style) Render(strs ...string) string { fg = s.getAsColor(foregroundKey) bg = s.getAsColor(backgroundKey) + ul = s.getAsColor(underlineColorKey) + underline = s.ul != UnderlineNone width = s.getAsInt(widthKey) height = s.getAsInt(heightKey) horizontalAlign = s.getAsPosition(alignHorizontalKey) @@ -268,6 +299,9 @@ func (s Style) Render(strs ...string) string { bottomPadding = s.getAsInt(paddingBottomKey) leftPadding = s.getAsInt(paddingLeftKey) + horizontalBorderSize = s.GetHorizontalBorderSize() + verticalBorderSize = s.GetVerticalBorderSize() + colorWhitespace = s.getAsBool(colorWhitespaceKey, true) inline = s.getAsBool(inlineKey, false) maxWidth = s.getAsInt(maxWidthKey) @@ -284,6 +318,8 @@ func (s Style) Render(strs ...string) string { useSpaceStyler = (underline && !underlineSpaces) || (strikethrough && !strikethroughSpaces) || underlineSpaces || strikethroughSpaces transform = s.getAsTransform(transformKey) + + link, linkParams = s.GetHyperlink() ) if transform != nil { @@ -294,62 +330,68 @@ func (s Style) Render(strs ...string) string { return s.maybeConvertTabs(str) } - // Enable support for ANSI on the legacy Windows cmd.exe console. This is a - // no-op on non-Windows systems and on Windows runs only once. - enableLegacyWindowsANSI() - if bold { te = te.Bold() } if italic { - te = te.Italic() + te = te.Italic(true) } if underline { - te = te.Underline() + te = te.Underline(true) } if reverse { - teWhitespace = teWhitespace.Reverse() - te = te.Reverse() + teWhitespace = teWhitespace.Reverse(true) + te = te.Reverse(true) } if blink { - te = te.Blink() + te = te.Blink(true) } if faint { te = te.Faint() } if fg != noColor { - te = te.Foreground(fg.color(s.r)) + te = te.ForegroundColor(fg) if styleWhitespace { - teWhitespace = teWhitespace.Foreground(fg.color(s.r)) + teWhitespace = teWhitespace.ForegroundColor(fg) } if useSpaceStyler { - teSpace = teSpace.Foreground(fg.color(s.r)) + teSpace = teSpace.ForegroundColor(fg) } } if bg != noColor { - te = te.Background(bg.color(s.r)) + te = te.BackgroundColor(bg) + if colorWhitespace { + teWhitespace = teWhitespace.BackgroundColor(bg) + } + if useSpaceStyler { + teSpace = teSpace.BackgroundColor(bg) + } + } + + if ul != noColor { + te = te.UnderlineColor(ul) if colorWhitespace { - teWhitespace = teWhitespace.Background(bg.color(s.r)) + teWhitespace = teWhitespace.UnderlineColor(ul) } if useSpaceStyler { - teSpace = teSpace.Background(bg.color(s.r)) + teSpace = teSpace.UnderlineColor(ul) } } if underline { - te = te.Underline() + te = te.UnderlineStyle(s.ul) } if strikethrough { - te = te.CrossOut() + te = te.Strikethrough(true) } if underlineSpaces { - teSpace = teSpace.Underline() + teSpace = teSpace.Underline(true) } if strikethroughSpaces { - teSpace = teSpace.CrossOut() + teSpace = teSpace.Strikethrough(true) } // Potentially convert tabs to spaces @@ -362,21 +404,30 @@ func (s Style) Render(strs ...string) string { str = strings.ReplaceAll(str, "\n", "") } + // Include borders in block size. + width -= horizontalBorderSize + height -= verticalBorderSize + // Word wrap if !inline && width > 0 { wrapAt := width - leftPadding - rightPadding - str = cellbuf.Wrap(str, wrapAt, "") + str = Wrap(str, wrapAt, "") } // Render core text { var b strings.Builder - l := strings.Split(str, "\n") - for i := range l { + isFirst := true + for line := range strings.SplitSeq(str, "\n") { + if isFirst { + isFirst = false + } else { + b.WriteRune('\n') + } if useSpaceStyler { // Look for spaces and apply a different styler - for _, r := range l[i] { + for _, r := range line { if unicode.IsSpace(r) { b.WriteString(teSpace.Styled(string(r))) continue @@ -384,32 +435,37 @@ func (s Style) Render(strs ...string) string { b.WriteString(te.Styled(string(r))) } } else { - b.WriteString(te.Styled(l[i])) - } - if i != len(l)-1 { - b.WriteRune('\n') + b.WriteString(te.Styled(line)) } } str = b.String() + + if len(link) > 0 { + str = ansi.SetHyperlink(link, linkParams) + str + ansi.ResetHyperlink() + } } // Padding if !inline { //nolint:nestif + padChar := s.paddingChar + if padChar == 0 { + padChar = ' ' + } if leftPadding > 0 { - var st *termenv.Style + var st *ansi.Style if colorWhitespace || styleWhitespace { st = &teWhitespace } - str = padLeft(str, leftPadding, st) + str = padLeft(str, leftPadding, st, padChar) } if rightPadding > 0 { - var st *termenv.Style + var st *ansi.Style if colorWhitespace || styleWhitespace { st = &teWhitespace } - str = padRight(str, rightPadding, st) + str = padRight(str, rightPadding, st, padChar) } if topPadding > 0 { @@ -433,7 +489,7 @@ func (s Style) Render(strs ...string) string { numLines := strings.Count(str, "\n") if numLines != 0 || width != 0 { - var st *termenv.Style + var st *ansi.Style if colorWhitespace || styleWhitespace { st = &teWhitespace } @@ -491,17 +547,21 @@ func (s Style) applyMargins(str string, inline bool) string { bottomMargin = s.getAsInt(marginBottomKey) leftMargin = s.getAsInt(marginLeftKey) - styler termenv.Style + style ansi.Style ) bgc := s.getAsColor(marginBackgroundKey) if bgc != noColor { - styler = styler.Background(bgc.color(s.r)) + style = style.BackgroundColor(bgc) } // Add left and right margin - str = padLeft(str, leftMargin, &styler) - str = padRight(str, rightMargin, &styler) + marginChar := s.marginChar + if marginChar == 0 { + marginChar = ' ' + } + str = padLeft(str, leftMargin, &style, marginChar) + str = padRight(str, rightMargin, &style, marginChar) // Top/bottom margin if !inline { @@ -509,10 +569,10 @@ func (s Style) applyMargins(str string, inline bool) string { spaces := strings.Repeat(" ", width) if topMargin > 0 { - str = styler.Styled(strings.Repeat(spaces+"\n", topMargin)) + str + str = style.Styled(strings.Repeat(spaces+"\n", topMargin)) + str } if bottomMargin > 0 { - str += styler.Styled(strings.Repeat("\n"+spaces, bottomMargin)) + str += style.Styled(strings.Repeat("\n"+spaces, bottomMargin)) } } @@ -520,65 +580,54 @@ func (s Style) applyMargins(str string, inline bool) string { } // Apply left padding. -func padLeft(str string, n int, style *termenv.Style) string { - return pad(str, -n, style) +func padLeft(str string, n int, style *ansi.Style, r rune) string { + return pad(str, -n, style, r) } // Apply right padding. -func padRight(str string, n int, style *termenv.Style) string { - return pad(str, n, style) +func padRight(str string, n int, style *ansi.Style, r rune) string { + return pad(str, n, style, r) } // pad adds padding to either the left or right side of a string. // Positive values add to the right side while negative values // add to the left side. -func pad(str string, n int, style *termenv.Style) string { +// r is the rune to use for padding. We use " " for margins and +// "\u00A0" for padding so that the padding is preserved when the +// string is copied and pasted. +func pad(str string, n int, style *ansi.Style, r rune) string { if n == 0 { return str } - sp := strings.Repeat(" ", abs(n)) + sp := strings.Repeat(string(r), abs(n)) if style != nil { sp = style.Styled(sp) } b := strings.Builder{} - l := strings.Split(str, "\n") - - for i := range l { + isFirst := true + for line := range strings.SplitSeq(str, "\n") { + if isFirst { + isFirst = false + } else { + b.WriteRune('\n') + } switch { // pad right case n > 0: - b.WriteString(l[i]) + b.WriteString(line) b.WriteString(sp) // pad left default: b.WriteString(sp) - b.WriteString(l[i]) - } - - if i != len(l)-1 { - b.WriteRune('\n') + b.WriteString(line) } } return b.String() } -func max(a, b int) int { //nolint:unparam,predeclared - if a > b { - return a - } - return b -} - -func min(a, b int) int { //nolint:predeclared - if a < b { - return a - } - return b -} - func abs(a int) int { if a < 0 { return -a diff --git a/vendor/charm.land/lipgloss/v2/terminal.go b/vendor/charm.land/lipgloss/v2/terminal.go new file mode 100644 index 00000000..a336abc7 --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/terminal.go @@ -0,0 +1,124 @@ +package lipgloss + +import ( + "fmt" + "image/color" + "io" + "strings" + "time" + + uv "github.com/charmbracelet/ultraviolet" + "github.com/charmbracelet/x/ansi" +) + +// queryBackgroundColor queries the terminal for the background color. +// If the terminal does not support querying the background color, nil is +// returned. +// +// Note: you will need to set the input to raw mode before calling this +// function. +// +// state, _ := term.MakeRaw(in.Fd()) +// defer term.Restore(in.Fd(), state) +// +// copied from x/term@v0.1.3. +func queryBackgroundColor(in io.Reader, out io.Writer) (c color.Color, err error) { + err = queryTerminal(in, out, defaultQueryTimeout, + func(seq string, pa *ansi.Parser) bool { + switch { + case ansi.HasOscPrefix(seq): + switch pa.Command() { + case 11: // OSC 11 + parts := strings.Split(string(pa.Data()), ";") + if len(parts) != 2 { + break // invalid, but we still need to parse the next sequence + } + c = ansi.XParseColor(parts[1]) + } + case ansi.HasCsiPrefix(seq): + switch pa.Command() { + case ansi.Command('?', 0, 'c'): // DA1 + return false + } + } + return true + }, ansi.RequestBackgroundColor+ansi.RequestPrimaryDeviceAttributes) + return +} + +const defaultQueryTimeout = time.Second * 2 + +// queryTerminalFilter is a function that filters input events using a type +// switch. If false is returned, the QueryTerminal function will stop reading +// input. +type queryTerminalFilter func(seq string, pa *ansi.Parser) bool + +// queryTerminal queries the terminal for support of various features and +// returns a list of response events. +// Most of the time, you will need to set stdin to raw mode before calling this +// function. +// Note: This function will block until the terminal responds or the timeout +// is reached. +// copied from x/term@v0.1.3. +func queryTerminal( + in io.Reader, + out io.Writer, + timeout time.Duration, + filter queryTerminalFilter, + query string, +) error { + // We use [uv.NewCancelReader] because it uses a different Windows + // implementation than the on in the [cancelreader] library, which uses + // the Cancel IO API to cancel reads instead of using Overlapped IO. + rd, err := uv.NewCancelReader(in) + if err != nil { + return fmt.Errorf("could not create cancel reader: %w", err) + } + + defer rd.Close() //nolint: errcheck + + done := make(chan struct{}, 1) + defer close(done) + go func() { + select { + case <-done: + case <-time.After(timeout): + rd.Cancel() + } + }() + + if _, err := io.WriteString(out, query); err != nil { + return fmt.Errorf("could not write query: %w", err) + } + + pa := ansi.GetParser() + defer ansi.PutParser(pa) + + var acc []byte // Accumulate partial responses before filtering + var buf [256]byte // 256 bytes should be enough for most responses + var state byte + for { + n, err := rd.Read(buf[:]) + if err != nil { + return fmt.Errorf("could not read from input: %w", err) + } + + p := buf[:] + for n > 0 { + seq, _, read, newState := ansi.DecodeSequence(p[:n], state, pa) + acc = append(acc, seq...) + + if newState == ansi.NormalState { + if !filter(string(acc), pa) { + return nil + } + + acc = acc[:0] + } + + state = newState + n -= read + p = p[read:] + } + } +} diff --git a/vendor/github.com/charmbracelet/lipgloss/unset.go b/vendor/charm.land/lipgloss/v2/unset.go similarity index 91% rename from vendor/github.com/charmbracelet/lipgloss/unset.go rename to vendor/charm.land/lipgloss/v2/unset.go index 1086e722..b81ee882 100644 --- a/vendor/github.com/charmbracelet/lipgloss/unset.go +++ b/vendor/charm.land/lipgloss/v2/unset.go @@ -19,8 +19,7 @@ func (s Style) UnsetItalic() Style { // UnsetUnderline removes the underline style rule, if set. func (s Style) UnsetUnderline() Style { - s.unset(underlineKey) - return s + return s.Underline(false) } // UnsetStrikethrough removes the strikethrough style rule, if set. @@ -96,6 +95,13 @@ func (s Style) UnsetPadding() Style { s.unset(paddingRightKey) s.unset(paddingTopKey) s.unset(paddingBottomKey) + s.unset(paddingCharKey) + return s +} + +// UnsetPaddingChar removes the padding character style rule, if set. +func (s Style) UnsetPaddingChar() Style { + s.unset(paddingCharKey) return s } @@ -237,6 +243,20 @@ func (s Style) UnsetBorderLeftForeground() Style { return s } +// UnsetBorderForegroundBlend removes the border blend foreground color rules, +// if set. +func (s Style) UnsetBorderForegroundBlend() Style { + s.unset(borderForegroundBlendKey) + return s +} + +// UnsetBorderForegroundBlendOffset removes the border blend offset style rule, +// if set. +func (s Style) UnsetBorderForegroundBlendOffset() Style { + s.unset(borderForegroundBlendOffsetKey) + return s +} + // UnsetBorderBackground removes all border background color styles, if // set. func (s Style) UnsetBorderBackground() Style { @@ -324,6 +344,14 @@ func (s Style) UnsetTransform() Style { return s } +// UnsetHyperlink removes the value set by Hyperlink. +func (s Style) UnsetHyperlink() Style { + s.unset(linkKey) + s.unset(linkParamsKey) + s.link, s.linkParams = "", "" // save memory + return s +} + // UnsetString sets the underlying string value to the empty string. func (s Style) UnsetString() Style { s.value = "" diff --git a/vendor/github.com/charmbracelet/lipgloss/whitespace.go b/vendor/charm.land/lipgloss/v2/whitespace.go similarity index 55% rename from vendor/github.com/charmbracelet/lipgloss/whitespace.go rename to vendor/charm.land/lipgloss/v2/whitespace.go index 040dc98e..e353a076 100644 --- a/vendor/github.com/charmbracelet/lipgloss/whitespace.go +++ b/vendor/charm.land/lipgloss/v2/whitespace.go @@ -4,24 +4,17 @@ import ( "strings" "github.com/charmbracelet/x/ansi" - "github.com/muesli/termenv" ) // whitespace is a whitespace renderer. type whitespace struct { - re *Renderer - style termenv.Style chars string + style Style } -// newWhitespace creates a new whitespace renderer. The order of the options -// matters, if you're using WithWhitespaceRenderer, make sure it comes first as -// other options might depend on it. -func newWhitespace(r *Renderer, opts ...WhitespaceOption) *whitespace { - w := &whitespace{ - re: r, - style: r.ColorProfile().String(), - } +// newWhitespace creates a new whitespace renderer. +func newWhitespace(opts ...WhitespaceOption) *whitespace { + w := &whitespace{} for _, opt := range opts { opt(w) } @@ -41,11 +34,18 @@ func (w whitespace) render(width int) string { // Cycle through runes and print them into the whitespace. for i := 0; i < width; { b.WriteRune(r[j]) + // Measure the width of the rune we just wrote, ensuring we always + // make progress to avoid infinite loops with zero-width characters + // like tabs. + runeWidth := ansi.StringWidth(string(r[j])) + if runeWidth < 1 { + runeWidth = 1 + } + i += runeWidth j++ if j >= len(r) { j = 0 } - i += ansi.StringWidth(string(r[j])) } // Fill any extra gaps white spaces. This might be necessary if any runes @@ -55,23 +55,16 @@ func (w whitespace) render(width int) string { b.WriteString(strings.Repeat(" ", short)) } - return w.style.Styled(b.String()) + return w.style.Render(b.String()) } // WhitespaceOption sets a styling rule for rendering whitespace. type WhitespaceOption func(*whitespace) -// WithWhitespaceForeground sets the color of the characters in the whitespace. -func WithWhitespaceForeground(c TerminalColor) WhitespaceOption { - return func(w *whitespace) { - w.style = w.style.Foreground(c.color(w.re)) - } -} - -// WithWhitespaceBackground sets the background color of the whitespace. -func WithWhitespaceBackground(c TerminalColor) WhitespaceOption { +// WithWhitespaceStyle sets the style for the whitespace. +func WithWhitespaceStyle(s Style) WhitespaceOption { return func(w *whitespace) { - w.style = w.style.Background(c.color(w.re)) + w.style = s } } diff --git a/vendor/charm.land/lipgloss/v2/wrap.go b/vendor/charm.land/lipgloss/v2/wrap.go new file mode 100644 index 00000000..ccffe89b --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/wrap.go @@ -0,0 +1,113 @@ +package lipgloss + +import ( + "bytes" + "io" + + uv "github.com/charmbracelet/ultraviolet" + "github.com/charmbracelet/x/ansi" +) + +// Wrap wraps the given string to the given width, preserving ANSI styles and links. +func Wrap(s string, width int, breakpoints string) string { + var buf bytes.Buffer + s = ansi.Wrap(s, width, breakpoints) + w := NewWrapWriter(&buf) + defer w.Close() //nolint:errcheck + _, _ = io.WriteString(w, s) + return buf.String() +} + +// WrapWriter is a writer that writes to a buffer and keeps track of the +// current pen style and link state for the purpose of wrapping with newlines. +// +// When it encounters a newline, it resets the style and link, writes the +// newline, and then reapplies the style and link to the next line. +type WrapWriter struct { + w io.Writer + p *ansi.Parser + style uv.Style + link uv.Link +} + +// NewWrapWriter returns a new [WrapWriter]. +func NewWrapWriter(w io.Writer) *WrapWriter { + pw := &WrapWriter{w: w} + pw.p = ansi.GetParser() + handleCsi := func(cmd ansi.Cmd, params ansi.Params) { + if cmd == 'm' { + uv.ReadStyle(params, &pw.style) + } + } + handleOsc := func(cmd int, data []byte) { + if cmd == 8 { + uv.ReadLink(data, &pw.link) + } + } + pw.p.SetHandler(ansi.Handler{ + HandleCsi: handleCsi, + HandleOsc: handleOsc, + }) + return pw +} + +// Style returns the current pen style. +func (w *WrapWriter) Style() uv.Style { + return w.style +} + +// Link returns the current pen link. +func (w *WrapWriter) Link() uv.Link { + return w.link +} + +// Write writes to the buffer. +func (w *WrapWriter) Write(p []byte) (int, error) { + if w.p == nil { + // The writer has been closed and its parser returned to the pool. + // Writing after close can happen during out-of-order teardown of + // nested writer chains; treat it as a no-op rather than panicking. + return len(p), nil + } + for i := range p { + b := p[i] + w.p.Advance(b) + if b == '\n' { + if !w.style.IsZero() { + _, _ = w.w.Write([]byte(ansi.ResetStyle)) + } + if !w.link.IsZero() { + _, _ = w.w.Write([]byte(ansi.ResetHyperlink())) + } + } + + _, _ = w.w.Write([]byte{b}) + if b == '\n' { + if !w.link.IsZero() { + _, _ = w.w.Write([]byte(ansi.SetHyperlink(w.link.URL, w.link.Params))) + } + if !w.style.IsZero() { + _, _ = w.w.Write([]byte(w.style.String())) + } + } + } + + return len(p), nil +} + +// Close closes the writer, resets the style and link if necessary, and releases +// its parser. Calling it is performance critical, but forgetting it does not +// cause safety issues or leaks. +func (w *WrapWriter) Close() error { + if !w.style.IsZero() { + _, _ = w.w.Write([]byte(ansi.ResetStyle)) + } + if !w.link.IsZero() { + _, _ = w.w.Write([]byte(ansi.ResetHyperlink())) + } + if w.p != nil { + ansi.PutParser(w.p) + w.p = nil + } + return nil +} diff --git a/vendor/charm.land/lipgloss/v2/writer.go b/vendor/charm.land/lipgloss/v2/writer.go new file mode 100644 index 00000000..78b48b8d --- /dev/null +++ b/vendor/charm.land/lipgloss/v2/writer.go @@ -0,0 +1,160 @@ +package lipgloss + +import ( + "bytes" + "fmt" + "io" + "os" + + "github.com/charmbracelet/colorprofile" +) + +// Writer is the default writer that prints to stdout, automatically +// downsampling colors when necessary. +var Writer = colorprofile.NewWriter(os.Stdout, os.Environ()) + +// Println to stdout, automatically downsampling colors when necessary, ending +// with a trailing newline. +// +// Example: +// +// str := NewStyle(). +// Foreground(lipgloss.Color("#6a00ff")). +// Render("breakfast") +// +// Println("Time for a", str, "sandwich!") +func Println(v ...any) (int, error) { + return fmt.Fprintln(Writer, v...) //nolint:wrapcheck +} + +// Printf prints formatted text to stdout, automatically downsampling colors +// when necessary. +// +// Example: +// +// str := NewStyle(). +// Foreground(lipgloss.Color("#6a00ff")). +// Render("knuckle") +// +// Printf("Time for a %s sandwich!\n", str) +func Printf(format string, v ...any) (int, error) { + return fmt.Fprintf(Writer, format, v...) //nolint:wrapcheck +} + +// Print to stdout, automatically downsampling colors when necessary. +// +// Example: +// +// str := NewStyle(). +// Foreground(lipgloss.Color("#6a00ff")). +// Render("Who wants marmalade?\n") +// +// Print(str) +func Print(v ...any) (int, error) { + return fmt.Fprint(Writer, v...) //nolint:wrapcheck +} + +// Fprint pritnts to the given writer, automatically downsampling colors when +// necessary. +// +// Example: +// +// str := NewStyle(). +// Foreground(lipgloss.Color("#6a00ff")). +// Render("guzzle") +// +// Fprint(os.Stderr, "I %s horchata pretty much all the time.\n", str) +func Fprint(w io.Writer, v ...any) (int, error) { + return fmt.Fprint(colorprofile.NewWriter(w, os.Environ()), v...) //nolint:wrapcheck +} + +// Fprintln prints to the given writer, automatically downsampling colors when +// necessary, and ending with a trailing newline. +// +// Example: +// +// str := NewStyle(). +// Foreground(lipgloss.Color("#6a00ff")). +// Render("Sandwich time!") +// +// Fprintln(os.Stderr, str) +func Fprintln(w io.Writer, v ...any) (int, error) { + return fmt.Fprintln(colorprofile.NewWriter(w, os.Environ()), v...) //nolint:wrapcheck +} + +// Fprintf prints text to a writer, against the given format, automatically +// downsampling colors when necessary. +// +// Example: +// +// str := NewStyle(). +// Foreground(lipgloss.Color("#6a00ff")). +// Render("artichokes") +// +// Fprintf(os.Stderr, "I really love %s!\n", food) +func Fprintf(w io.Writer, format string, v ...any) (int, error) { + return fmt.Fprintf(colorprofile.NewWriter(w, os.Environ()), format, v...) //nolint:wrapcheck +} + +// Sprint returns a string for stdout, automatically downsampling colors when +// necessary. +// +// Example: +// +// str := NewStyle(). +// Faint(true). +// Foreground(lipgloss.Color("#6a00ff")). +// Render("I love to eat") +// +// str = Sprint(str) +func Sprint(v ...any) string { + var buf bytes.Buffer + w := colorprofile.Writer{ + Forward: &buf, + Profile: Writer.Profile, + } + fmt.Fprint(&w, v...) //nolint:errcheck + return buf.String() +} + +// Sprintln returns a string for stdout, automatically downsampling colors when +// necessary, and ending with a trailing newline. +// +// Example: +// +// str := NewStyle(). +// Bold(true). +// Foreground(lipgloss.Color("#6a00ff")). +// Render("Yummy!") +// +// str = Sprintln(str) +func Sprintln(v ...any) string { + var buf bytes.Buffer + w := colorprofile.Writer{ + Forward: &buf, + Profile: Writer.Profile, + } + fmt.Fprintln(&w, v...) //nolint:errcheck + return buf.String() +} + +// Sprintf returns a formatted string for stdout, automatically downsampling +// colors when necessary. +// +// Example: +// +// str := NewStyle(). +// Bold(true). +// Foreground(lipgloss.Color("#fccaee")). +// Render("Cantaloupe") +// +// str = Sprintf("I really love %s!", str) +func Sprintf(format string, v ...any) string { + var buf bytes.Buffer + w := colorprofile.Writer{ + Forward: &buf, + Profile: Writer.Profile, + } + fmt.Fprintf(&w, format, v...) //nolint:errcheck + return buf.String() +} diff --git a/vendor/github.com/muesli/termenv/LICENSE b/vendor/dev.gaijin.team/go/exhaustruct/v5/LICENSE similarity index 96% rename from vendor/github.com/muesli/termenv/LICENSE rename to vendor/dev.gaijin.team/go/exhaustruct/v5/LICENSE index 8532c45c..6698196c 100644 --- a/vendor/github.com/muesli/termenv/LICENSE +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Christian Muehlhaeuser +Copyright (c) 2022 Gaijin Entertainment 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/dev.gaijin.team/go/exhaustruct/v5/analyzer/analyzer.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/analyzer.go new file mode 100644 index 00000000..ef1bc9c5 --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/analyzer.go @@ -0,0 +1,112 @@ +package analyzer + +import ( + "flag" + "sync" + + "dev.gaijin.team/go/golib/e" + "dev.gaijin.team/go/golib/fields" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + + "dev.gaijin.team/go/exhaustruct/v5/internal/astutil" + "dev.gaijin.team/go/exhaustruct/v5/internal/directive" + "dev.gaijin.team/go/exhaustruct/v5/internal/pattern" + "dev.gaijin.team/go/exhaustruct/v5/internal/structure" +) + +// NewAnalyzer returns an analyzer configured exclusively through command-line +// flags, intended for CLI drivers (singlechecker, go vet -vettool). The +// processor is built lazily on the first run, after the driver has parsed the +// flags. +func NewAnalyzer() *analysis.Analyzer { + config := &Config{} + + lazyProcessor := sync.OnceValues(func() (*structure.Processor, error) { + return newProcessor(config) + }) + + a := newBaseAnalyzer(func(pass *analysis.Pass) (any, error) { + processor, err := lazyProcessor() + if err != nil { + return nil, err + } + + run(pass, config, processor) + + return nil, nil //nolint:nilnil + }) + + a.Flags.Init("", flag.PanicOnError) + config.bindToFlagSet(&a.Flags) + + return a +} + +// NewAnalyzerWithConfig returns an analyzer configured programmatically, +// intended for library consumers such as golangci-lint. The configuration is +// copied and validated immediately; it exposes no flags, and later mutations +// of the passed Config have no effect. +func NewAnalyzerWithConfig(config Config) (*analysis.Analyzer, error) { + processor, err := newProcessor(&config) + if err != nil { + return nil, err + } + + return newBaseAnalyzer(func(pass *analysis.Pass) (any, error) { + run(pass, &config, processor) + + return nil, nil //nolint:nilnil + }), nil +} + +func newBaseAnalyzer(run func(*analysis.Pass) (any, error)) *analysis.Analyzer { + return &analysis.Analyzer{ //nolint:exhaustruct + Name: "exhaustruct", + Doc: "Checks if all structure fields are initialized", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + } +} + +func newProcessor(config *Config) (*structure.Processor, error) { + enforce, err := pattern.NewList(config.EnforcePatterns...) + if err != nil { + return nil, e.NewFrom("compile enforce patterns", err, fields.F("flag", "enforce-rx")) + } + + ignore, err := pattern.NewList(config.IgnorePatterns...) + if err != nil { + return nil, e.NewFrom("compile ignore patterns", err, fields.F("flag", "ignore-rx")) + } + + optional, err := pattern.NewList(config.OptionalPatterns...) + if err != nil { + return nil, e.NewFrom("compile optional patterns", err, fields.F("flag", "optional-rx")) + } + + allowEmpty, err := pattern.NewList(config.AllowEmptyPatterns...) + if err != nil { + return nil, e.NewFrom("compile allow-empty patterns", err, fields.F("flag", "allow-empty-rx")) + } + + fp := astutil.NewFileParser() + + return structure.NewProcessor( + directive.NewScanner(fp), + structure.NewOriginScanner(fp), + structure.WithEnforce(enforce), + structure.WithIgnore(ignore), + structure.WithOptional(optional), + structure.WithAllowEmpty(allowEmpty), + ), nil +} + +func run(pass *analysis.Pass, config *Config, processor *structure.Processor) { + for _, diag := range processor.Directives().ProcessFiles(pass.Fset, pass.Files...) { + pass.Report(diag) + } + + newMissingFieldsVisitor(pass, config, processor).run() + runTagMigration(pass) +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/config.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/config.go new file mode 100644 index 00000000..82101bc3 --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/config.go @@ -0,0 +1,134 @@ +package analyzer + +import ( + "flag" + "strings" + + "dev.gaijin.team/go/exhaustruct/v5/internal/pattern" +) + +type Config struct { + // EnforcePatterns is a list of regular expressions to match type names that + // should be checked. Anonymous structs can be matched by '' alias. + // + // Each regular expression must match the full type name, including package path. + // For example, to match type `net/http.Cookie` regular expression should be + // `.*/http\.Cookie`, but not `http\.Cookie`. + EnforcePatterns Patterns `exhaustruct:"optional"` + + // IgnorePatterns is a list of regular expressions to match type names that + // should be skipped from checking. Anonymous structs can be matched by + // '' alias. + // + // Has precedence over EnforcePatterns. + // + // Each regular expression must match the full type name, including package path. + // For example, to match type `net/http.Cookie` regular expression should be + // `.*/http\.Cookie`, but not `http\.Cookie`. + IgnorePatterns Patterns `exhaustruct:"optional"` + + // OptionalPatterns is a list of regular expressions to match type names where + // all fields are treated as optional. Anonymous structs can be matched by + // '' alias. + // + // Each regular expression must match the full type name, including package path. + // For example, to match type `net/http.Cookie` regular expression should be + // `.*/http\.Cookie`, but not `http\.Cookie`. + OptionalPatterns Patterns `exhaustruct:"optional"` + + // AllowEmpty allows empty structures, effectively excluding them from the check. + AllowEmpty bool `exhaustruct:"optional"` + + // AllowEmptyPatterns is a list of regular expressions to match type names that + // should be allowed to be empty. Anonymous structs can be matched by + // '' alias. + // + // Each regular expression must match the full type name, including package path. + // For example, to match type `net/http.Cookie` regular expression should be + // `.*/http\.Cookie`, but not `http\.Cookie`. + AllowEmptyPatterns Patterns `exhaustruct:"optional"` + + // AllowEmptyReturns allows empty structures in return statements. + AllowEmptyReturns bool `exhaustruct:"optional"` + + // AllowEmptyDeclarations allows empty structures in variable declarations. + AllowEmptyDeclarations bool `exhaustruct:"optional"` + + // ReportFullTypePath enables full package path in error messages instead of + // short package name. This helps when configuring include/exclude patterns, + // as import aliases can make short names ambiguous. + ReportFullTypePath bool `exhaustruct:"optional"` + + // ExplicitMode enables opt-in checking. When true, only types marked with + // //exhaustruct:enforce directive or matching enforce-rx patterns are checked. + ExplicitMode bool `exhaustruct:"optional"` +} + +// bindToFlagSet binds the config fields to the provided flag set. +func (c *Config) bindToFlagSet(fs *flag.FlagSet) { + fs.BoolVar(&c.ExplicitMode, "explicit", c.ExplicitMode, + "Enable explicit mode: only check types marked with //exhaustruct:enforce "+ + "directive or matching -enforce-rx patterns") + + fs.Var(&c.EnforcePatterns, "enforce-rx", + "Regular expression to match type names that should be checked. "+ + "Anonymous structs can be matched by '' alias. "+ + "Each regex must match the full type name including package path. "+ + "Example: `.*/http\\.Cookie`. Can be used multiple times.") + + fs.Var(&c.IgnorePatterns, "ignore-rx", + "Regular expression to skip type names from checking, has precedence over -enforce-rx. "+ + "Anonymous structs can be matched by '' alias. "+ + "Each regex must match the full type name including package path. "+ + "Example: `.*/http\\.Cookie`. Can be used multiple times.") + + fs.Var(&c.OptionalPatterns, "optional-rx", + "Regular expression to match type names where all fields are optional. "+ + "Anonymous structs can be matched by '' alias. "+ + "Each regex must match the full type name including package path. "+ + "Example: `.*/http\\.Cookie`. Can be used multiple times.") + + fs.BoolVar(&c.AllowEmpty, "allow-empty", c.AllowEmpty, + "Allow empty structures, effectively excluding them from the check") + + fs.Var(&c.AllowEmptyPatterns, "allow-empty-rx", + "Regular expression to match type names that should be allowed to be empty. "+ + "Anonymous structs can be matched by '' alias. "+ + "Each regex must match the full type name including package path. "+ + "Example: `.*/http\\.Cookie`. Can be used multiple times.") + + fs.BoolVar(&c.AllowEmptyReturns, "allow-empty-returns", c.AllowEmptyReturns, + "Allow empty structures in return statements") + + fs.BoolVar(&c.AllowEmptyDeclarations, "allow-empty-declarations", c.AllowEmptyDeclarations, + "Allow empty structures in variable declarations") + + fs.BoolVar(&c.ReportFullTypePath, "report-full-type-path", c.ReportFullTypePath, + "Report full package path in error messages (e.g., 'net/http.Cookie' instead of 'http.Cookie'). "+ + "Useful for identifying types when configuring enforce/ignore patterns.") +} + +// Patterns is a list of regular expression patterns. It implements +// flag.Value, validating each value as a regular expression at +// flag-parse time so invalid patterns fail early. +type Patterns []string + +// String returns the patterns joined with commas (flag.Value interface). +func (p *Patterns) String() string { + if p == nil { + return "" + } + + return strings.Join(*p, ",") +} + +// Set validates and appends a pattern (flag.Value interface). +func (p *Patterns) Set(value string) error { + if _, err := pattern.NewList(value); err != nil { + return err //nolint:wrapcheck + } + + *p = append(*p, value) + + return nil +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/missing-fields-visitor.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/missing-fields-visitor.go new file mode 100644 index 00000000..558d8092 --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/missing-fields-visitor.go @@ -0,0 +1,381 @@ +package analyzer + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "slices" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" + + "dev.gaijin.team/go/exhaustruct/v5/internal/directive" + "dev.gaijin.team/go/exhaustruct/v5/internal/structure" +) + +// missingFieldsVisitor checks struct literals for missing field initializations. +type missingFieldsVisitor struct { + pass *analysis.Pass + config *Config + processor *structure.Processor +} + +func newMissingFieldsVisitor( + pass *analysis.Pass, + config *Config, + processor *structure.Processor, +) *missingFieldsVisitor { + return &missingFieldsVisitor{ + pass: pass, + config: config, + processor: processor, + } +} + +func (v *missingFieldsVisitor) run() { + insp := v.pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) //nolint:forcetypeassert + + insp.WithStack([]ast.Node{(*ast.CompositeLit)(nil)}, v.visit) +} + +func (v *missingFieldsVisitor) visit(n ast.Node, push bool, stack []ast.Node) bool { + if !push { + return true + } + + lit, ok := n.(*ast.CompositeLit) + if !ok { + return true + } + + lv := literalVisitor{missingFieldsVisitor: v, lit: lit, stack: stack} + lv.process() + + return true +} + +// literalVisitor carries context for processing a single composite literal. +type literalVisitor struct { + *missingFieldsVisitor + + lit *ast.CompositeLit + stack []ast.Node +} + +// literal holds resolved info for a struct literal being checked. +type literal struct { + strct *structure.Struct + ignored bool + enforced bool +} + +// shouldCheck implements checking decision priority. +func (l literal) shouldCheck(explicitMode bool) bool { + if l.ignored { + return false + } + + if l.enforced { + return true + } + + if l.strct.IsIgnored() { + return false + } + + if l.strct.IsEnforced() { + return true + } + + return !explicitMode +} + +func (lv literalVisitor) process() { + lit, ok := lv.resolveLiteral() + if !ok { + return + } + + if len(lv.lit.Elts) == 0 && lv.checkEmptyAllowed(lit.strct) { + return + } + + if pos, msg := lv.checkLiteral(lit); pos != nil { + lv.pass.Reportf(*pos, "%s", msg) + } +} + +// resolveLiteral extracts struct type information from the composite literal, +// retrieves cached metadata, and looks up directives. +func (lv literalVisitor) resolveLiteral() (lit literal, ok bool) { + typeName, strct, pos := lv.resolveLiteralType() + if strct == nil { + return literal{}, false //nolint:exhaustruct + } + + s, diags := lv.processor.ResolveStruct( + lv.pass.Fset, typeName, strct, pos, lv.pass.Pkg, + ) + + for _, diag := range diags { + lv.pass.Report(diag) + } + + if s == nil { + return literal{}, false //nolint:exhaustruct + } + + litPos := lv.pass.Fset.Position(lv.lit.Pos()) + dirs, dirDiags := lv.processor.Directives().Lookup(lv.pass.Fset, litPos) + + for _, d := range dirDiags { + lv.pass.Report(d) + } + + return literal{ + strct: s, + ignored: dirs.Contains(directive.Ignore), + enforced: dirs.Contains(directive.Enforce), + }, true +} + +// resolveLiteralType resolves the composite literal's type and definition position. +func (lv literalVisitor) resolveLiteralType() (name *types.TypeName, strct *types.Struct, pos token.Pos) { + typ := lv.pass.TypesInfo.TypeOf(lv.lit) + + if ptr, ok := typ.(*types.Pointer); ok { + typ = ptr.Elem() + } + + switch t := typ.(type) { + case *types.Alias: + name = t.Obj() + case *types.Named: + name = t.Obj() + } + + typ = types.Unalias(typ) + + switch t := typ.(type) { + case *types.Named: + var ok bool + + if strct, ok = t.Underlying().(*types.Struct); !ok { + return nil, nil, token.NoPos + } + + pos = name.Pos() + + return name, strct, pos + + case *types.Struct: + pos = lv.findAnonymousStructPos() + + return name, t, pos + + default: + return nil, nil, token.NoPos + } +} + +// findAnonymousStructPos finds the position of the struct keyword for anonymous structs. +func (lv literalVisitor) findAnonymousStructPos() token.Pos { + if lv.lit.Type != nil { + if st, ok := lv.lit.Type.(*ast.StructType); ok { + return st.Struct + } + + return token.NoPos + } + + for i := len(lv.stack) - 2; i >= 0; i-- { //nolint:mnd + switch parent := lv.stack[i].(type) { + case *ast.KeyValueExpr: + continue + + case *ast.CompositeLit: + return structPosFromType(parent.Type) + + default: + return token.NoPos + } + } + + return token.NoPos +} + +func structPosFromType(typ ast.Expr) token.Pos { + if typ == nil { + return token.NoPos + } + + switch t := typ.(type) { + case *ast.ArrayType: + return structPosFromExpr(t.Elt) + + case *ast.MapType: + return structPosFromExpr(t.Value) + } + + return token.NoPos +} + +func structPosFromExpr(expr ast.Expr) token.Pos { + if star, ok := expr.(*ast.StarExpr); ok { + expr = star.X + } + + if st, ok := expr.(*ast.StructType); ok { + return st.Struct + } + + return token.NoPos +} + +func (lv literalVisitor) checkEmptyAllowed(s *structure.Struct) bool { + if lv.config.AllowEmpty { + return true + } + + if s.AllowEmptyDecl { + return true + } + + if ret, ok := lv.getParentReturnStmt(); ok { + if lv.config.AllowEmptyReturns { + return true + } + + if lv.isErrorReturnStatement(ret) { + return true + } + } + + if lv.isChildOfVariableDeclaration() && lv.config.AllowEmptyDeclarations { + return true + } + + return false +} + +func (lv literalVisitor) checkLiteral(lit literal) (*token.Pos, string) { + if !lit.shouldCheck(lv.config.ExplicitMode) { + return nil, "" + } + + strct := lit.strct + + missingFields := strct.SkippedFields(lv.lit, lv.pass.Pkg.Path()) + + if len(missingFields) == 0 { + return nil, "" + } + + pos := lv.lit.Pos() + + displayName := strct.PackageName + "." + strct.Name + if lv.config.ReportFullTypePath { + displayName = strct.FullPath + } + + if len(missingFields) == 1 { + return &pos, fmt.Sprintf("%s is missing field %s", displayName, structure.FormatFieldNames(missingFields)) + } + + return &pos, fmt.Sprintf("%s is missing fields %s", displayName, structure.FormatFieldNames(missingFields)) +} + +func (lv literalVisitor) isChildOfVariableDeclaration() bool { + if len(lv.stack) < 2 { //nolint:mnd + return false + } + + for i := len(lv.stack) - 1; i > 0; i-- { + parent := lv.stack[i-1] + + switch p := parent.(type) { + case *ast.AssignStmt: + if p.Tok == token.DEFINE { + return true + } + + case *ast.ValueSpec: + return true + + case *ast.UnaryExpr: + if p.Op == token.AND { + continue + } + + return false + + default: + return false + } + } + + return false +} + +func (lv literalVisitor) getParentReturnStmt() (*ast.ReturnStmt, bool) { + if len(lv.stack) < 2 { //nolint:mnd + return nil, false + } + + for i := len(lv.stack) - 1; i > 0; i-- { + parent := lv.stack[i-1] + + switch p := parent.(type) { + case *ast.ReturnStmt: + return p, true + + case *ast.UnaryExpr: + if p.Op == token.AND { + continue + } + + return nil, false + + default: + return nil, false + } + } + + return nil, false +} + +//nolint:forcetypeassert,gochecknoglobals +var builtinErrorInterface = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) + +func (lv literalVisitor) isErrorReturnStatement(n *ast.ReturnStmt) bool { + if len(n.Results) == 0 { + return false + } + + for _, ri := range slices.Backward(n.Results) { + if ri == lv.lit { + continue + } + + switch ri := ri.(type) { + case *ast.Ident: + if ri.Name == "nil" { + continue + } + + case *ast.UnaryExpr: + if ri.X == lv.lit { + continue + } + } + + resultType := lv.pass.TypesInfo.TypeOf(ri) + if resultType != nil && types.Implements(resultType, builtinErrorInterface) { + return true + } + } + + return false +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/tag-migration-visitor.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/tag-migration-visitor.go new file mode 100644 index 00000000..7e75b668 --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/analyzer/tag-migration-visitor.go @@ -0,0 +1,111 @@ +package analyzer + +import ( + "go/ast" + "reflect" + "regexp" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +// runTagMigration scans struct definitions for deprecated exhaustruct tags +// and emits migration diagnostics with suggested fixes, using inspector to +// traverse StructType nodes efficiently. +func runTagMigration(pass *analysis.Pass) { + insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) //nolint:forcetypeassert + + insp.Preorder([]ast.Node{new(ast.StructType)}, func(n ast.Node) { + visitStructType(pass, n) + }) +} + +func visitStructType(pass *analysis.Pass, n ast.Node) { + st, ok := n.(*ast.StructType) + if !ok { + return + } + + if st.Fields == nil { + return + } + + for _, field := range st.Fields.List { + if field.Tag == nil { + continue + } + + value, ok := parseExhaustructTag(field.Tag.Value) + if !ok { + continue + } + + pass.Report(buildTagDiagnostic(field, value)) + } +} + +const exhaustructTagKey = "exhaustruct" + +// parseExhaustructTag extracts value from `exhaustruct:"value"` tag. +// Returns ("", false) if tag not present. +func parseExhaustructTag(tagLiteral string) (string, bool) { + if len(tagLiteral) < 2 { //nolint:mnd + return "", false + } + + // Strip backticks + inner := tagLiteral[1 : len(tagLiteral)-1] + + return reflect.StructTag(inner).Lookup(exhaustructTagKey) +} + +func buildTagDiagnostic(field *ast.Field, tagValue string) analysis.Diagnostic { + return analysis.Diagnostic{ + Pos: field.Tag.Pos(), + Message: `struct tag "exhaustruct" is not supported anymore, use comment directives`, + SuggestedFixes: []analysis.SuggestedFix{buildTagFix(field, tagValue)}, + } +} + +func buildTagFix(field *ast.Field, tagValue string) analysis.SuggestedFix { + tag := field.Tag + newTag := removeExhaustructFromTag(tag.Value) + + if tagValue == "optional" { + if newTag != "" { + newTag += " " + } + + newTag += "//exhaustruct:optional" + } + + // Calculate start position (include leading space if removing entirely) + startPos := tag.Pos() + if newTag == "" { + startPos = field.Type.End() + } + + return analysis.SuggestedFix{ + Message: "fix", + TextEdits: []analysis.TextEdit{{ + Pos: startPos, + End: tag.End(), + NewText: []byte(newTag), + }}, + } +} + +var exhaustructTagPattern = regexp.MustCompile(`\s*exhaustruct:"[^"]*"`) + +func removeExhaustructFromTag(tagLiteral string) string { + tagLiteral = tagLiteral[1 : len(tagLiteral)-1] + tagLiteral = strings.TrimSpace(exhaustructTagPattern.ReplaceAllString(tagLiteral, "")) + + if tagLiteral == "" { + return "" + } + + return "`" + tagLiteral + "`" +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/astutil/file-parser.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/astutil/file-parser.go new file mode 100644 index 00000000..dc437fb2 --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/astutil/file-parser.go @@ -0,0 +1,213 @@ +// Package astutil provides AST file parsing utilities for the analyzer. +package astutil + +import ( + "go/ast" + "go/build" + "go/parser" + "go/token" + "os" + "path/filepath" + "strings" + "sync" + "sync/atomic" + + "dev.gaijin.team/go/golib/e" + "dev.gaijin.team/go/golib/fields" + "golang.org/x/tools/go/analysis" +) + +type ParseCallback func(fset *token.FileSet, file *ast.File) []analysis.Diagnostic + +// FileParser orchestrates AST parsing by triggering registered callbacks. +// Each file is processed only once. +// +// Safe for concurrent use. +type FileParser struct { + mu sync.RWMutex `exhaustruct:"optional"` + parsed map[string]bool + callbacks []ParseCallback `exhaustruct:"optional"` + parseFlags parser.Mode `exhaustruct:"optional"` + hits atomic.Uint64 `exhaustruct:"optional"` + misses atomic.Uint64 `exhaustruct:"optional"` +} + +type Option func(*FileParser) + +// WithParseFlags sets parser flags for file parsing. +// Default: parser.ParseComments | parser.SkipObjectResolution. +func WithParseFlags(flags parser.Mode) Option { + return func(p *FileParser) { p.parseFlags = flags } +} + +const parsedCachePrealloc = 64 + +func NewFileParser(opts ...Option) *FileParser { + p := &FileParser{ + parsed: make(map[string]bool, parsedCachePrealloc), + parseFlags: parser.ParseComments | parser.SkipObjectResolution, + } + + for _, opt := range opts { + opt(p) + } + + return p +} + +func (p *FileParser) OnFileParsed(cb ParseCallback) { + p.callbacks = append(p.callbacks, cb) +} + +// ProcessFiles triggers all callbacks for each provided AST file. +// Already-parsed files are skipped. +func (p *FileParser) ProcessFiles(fset *token.FileSet, files ...*ast.File) []analysis.Diagnostic { + var allDiags []analysis.Diagnostic + + for _, file := range files { + filename := fset.Position(file.Pos()).Filename + + p.mu.RLock() + + alreadyParsed := p.parsed[filename] + p.mu.RUnlock() + + if alreadyParsed { + p.hits.Add(1) + + continue + } + + p.mu.Lock() + + if p.parsed[filename] { + p.mu.Unlock() + p.hits.Add(1) + + continue + } + + p.misses.Add(1) + + for _, cb := range p.callbacks { + allDiags = append(allDiags, cb(fset, file)...) + } + + p.parsed[filename] = true + p.mu.Unlock() + } + + return allDiags +} + +// ProcessFilename parses a file from disk and triggers all callbacks. +// Returns nil if already processed or if the file belongs to the Go +// distribution. +func (p *FileParser) ProcessFilename(fset *token.FileSet, filename string) []analysis.Diagnostic { + if isGoRootFile(filename) { + return nil + } + + p.mu.RLock() + + alreadyParsed := p.parsed[filename] + p.mu.RUnlock() + + if alreadyParsed { + p.hits.Add(1) + + return nil + } + + p.mu.Lock() + defer p.mu.Unlock() + + if p.parsed[filename] { + p.hits.Add(1) + + return nil + } + + p.misses.Add(1) + + file, err := p.parse(fset, filename) + if err != nil { + p.parsed[filename] = true + + return []analysis.Diagnostic{{ + Pos: token.NoPos, + Message: err.Error(), + }} + } + + var allDiags []analysis.Diagnostic + + for _, cb := range p.callbacks { + allDiags = append(allDiags, cb(fset, file)...) + } + + p.parsed[filename] = true + + return allDiags +} + +func (p *FileParser) Stats() (hits, misses, size uint64) { + p.mu.RLock() + + size = uint64(len(p.parsed)) + + p.mu.RUnlock() + + return p.hits.Load(), p.misses.Load(), size +} + +// goRootPlaceholder is the literal prefix the compiler records instead of the +// real GOROOT for files of the Go distribution, see cmd/internal/objabi.AbsFile. +// Positions loaded from export data carry it as-is, so such paths can never be +// opened. +const goRootPlaceholder = "$GOROOT" + +// isGoRootFile reports whether filename belongs to the Go distribution. Its +// sources carry no exhaustruct directives, so parsing them yields nothing and +// their definitions are treated as directive-free. +func isGoRootFile(filename string) bool { + return hasPathPrefix(filename, goRootPlaceholder) || + hasPathPrefix(filename, build.Default.GOROOT) +} + +// hasPathPrefix reports whether path starts with prefix at a path element +// boundary. Separators are normalized, since the compiler records slashes on +// every platform while GOROOT uses the native separator. An empty prefix, as +// an unset or root GOROOT normalizes to, matches nothing rather than +// everything. +func hasPathPrefix(path, prefix string) bool { + prefix = strings.TrimSuffix(filepath.ToSlash(prefix), "/") + if prefix == "" { + return false + } + + path = filepath.ToSlash(path) + + if !strings.HasPrefix(path, prefix) { + return false + } + + rest := path[len(prefix):] + + return rest == "" || rest[0] == '/' +} + +func (p *FileParser) parse(fset *token.FileSet, filename string) (*ast.File, error) { + //nolint:gosec // filename is derived from source code, not user input + content, err := os.ReadFile(filename) + if err != nil { + return nil, e.NewFrom("read file", err, fields.F("filename", filename)) + } + + file, err := parser.ParseFile(fset, filename, content, p.parseFlags) + if err != nil { + return nil, e.NewFrom("parse file", err, fields.F("filename", filename)) + } + + return file, nil +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/cache/cache.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/cache/cache.go new file mode 100644 index 00000000..c480c6b3 --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/cache/cache.go @@ -0,0 +1,100 @@ +// Package cache provides a generic thread-safe cache with hit/miss tracking. +package cache + +import ( + "sync" + "sync/atomic" +) + +type Cache[K comparable, V any] struct { + entries map[K]V + mu sync.RWMutex `exhaustruct:"optional"` + hits atomic.Uint64 `exhaustruct:"optional"` + misses atomic.Uint64 `exhaustruct:"optional"` +} + +func New[K comparable, V any](initialCapacity int) *Cache[K, V] { + return &Cache[K, V]{ + entries: make(map[K]V, initialCapacity), + } +} + +func (c *Cache[K, V]) Get(key K) (v V, ok bool) { + c.mu.RLock() + + v, ok = c.entries[key] + + c.mu.RUnlock() + + if ok { + c.hits.Add(1) + } + + return v, ok +} + +// Peek returns the value without updating hit/miss counters. Use it when the +// caller has already recorded the miss that triggered the fill and a follow-up +// read for the just-written entry must not inflate the hit rate. +func (c *Cache[K, V]) Peek(key K) (v V, ok bool) { + c.mu.RLock() + + v, ok = c.entries[key] + + c.mu.RUnlock() + + return v, ok +} + +// Set stores value and increments miss counter (caller computed the value). +func (c *Cache[K, V]) Set(key K, value V) { + c.mu.Lock() + + c.entries[key] = value + c.misses.Add(1) + + c.mu.Unlock() +} + +// GetOrSet uses double-check locking to avoid computing values that are +// cached between the initial read check and acquiring the write lock. +func (c *Cache[K, V]) GetOrSet(key K, compute func() V) V { + c.mu.RLock() + + if v, ok := c.entries[key]; ok { + c.mu.RUnlock() + c.hits.Add(1) + + return v + } + + c.mu.RUnlock() + + c.mu.Lock() + defer c.mu.Unlock() + + // Double-check after acquiring write lock. + if v, ok := c.entries[key]; ok { + c.hits.Add(1) + + return v + } + + c.misses.Add(1) + + v := compute() + + c.entries[key] = v + + return v +} + +func (c *Cache[K, V]) Stats() (hits, misses, size uint64) { + c.mu.RLock() + + size = uint64(len(c.entries)) + + c.mu.RUnlock() + + return c.hits.Load(), c.misses.Load(), size +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/directive/directive.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/directive/directive.go new file mode 100644 index 00000000..aa7f8d90 --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/directive/directive.go @@ -0,0 +1,95 @@ +package directive + +import ( + "slices" + "strings" + + "dev.gaijin.team/go/golib/e" +) + +var ( + ErrEmptyDirective = e.New("empty directive") + ErrUnknownDirective = e.New("unknown directive") + ErrDuplicateDirectives = e.New("duplicate directives") +) + +type Directive string + +const ( + // Ignore skips checking for a specific struct literal. + Ignore Directive = "ignore" + // Enforce forces check even if type is excluded. + Enforce Directive = "enforce" + // Optional marks a field as optional. + Optional Directive = "optional" +) + +func (d Directive) IsValid() bool { + switch d { + case Ignore, Enforce, Optional: + return true + + default: + return false + } +} + +// Directives represents a collection of directives for a single line. +// Multiple directives can be specified comma-separated: //exhaustruct:enforce,optional. +type Directives []Directive + +func (ds Directives) Contains(d Directive) bool { + return slices.Contains(ds, d) +} + +// directivePrefix is the exact prefix for exhaustruct directives. +// Format: //exhaustruct: [optional comment]. +const directivePrefix = "//exhaustruct:" + +func Parse(text string) (found bool, result Directives, errs []error) { + text, found = strings.CutPrefix(text, directivePrefix) + if !found { + return false, nil, nil + } + + if idx := strings.IndexAny(text, " \t\n"); idx > 0 { + text = text[:idx] + } + + if text == "" { + return true, nil, []error{ErrEmptyDirective} + } + + parts := strings.Split(text, ",") + + result = make(Directives, 0, len(parts)) + + var hasDups bool + + for _, part := range parts { + d := Directive(part) + + if !d.IsValid() { + errs = append(errs, ErrUnknownDirective.WithField("directive", d)) + continue + } + + // giving the resulting size, linear search would be most efficient + if slices.Contains(result, d) { + hasDups = true + continue + } + + result = append(result, d) + } + + if hasDups { + errs = append(errs, ErrDuplicateDirectives) + } + + if len(result) == 0 { + result = nil + } + + return true, result, errs +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/directive/scanner.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/directive/scanner.go new file mode 100644 index 00000000..0de61ad5 --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/directive/scanner.go @@ -0,0 +1,189 @@ +package directive + +import ( + "go/ast" + "go/token" + + "golang.org/x/tools/go/analysis" + + "dev.gaijin.team/go/exhaustruct/v5/internal/astutil" + "dev.gaijin.team/go/exhaustruct/v5/internal/cache" +) + +// Scanner provides thread-safe caching and lookup of file directives. +type Scanner struct { + parser *astutil.FileParser + cache *cache.Cache[string, fileDirectives] +} + +const cachePreallocSize = 64 + +// NewScanner creates a new directive scanner that registers a callback +// with the file parser to extract directives from parsed files. +func NewScanner(parser *astutil.FileParser) *Scanner { + s := &Scanner{ + parser: parser, + cache: cache.New[string, fileDirectives](cachePreallocSize), + } + + parser.OnFileParsed(s.onFileParsed) + + return s +} + +func (s *Scanner) onFileParsed(fset *token.FileSet, file *ast.File) []analysis.Diagnostic { + filename := fset.Position(file.Pos()).Filename + + fd, diags := s.parseFileDirectives(fset, file) + + s.cache.Set(filename, fd) + + return diags +} + +// ProcessFiles pre-populates the cache by delegating to FileParser.ProcessFiles. +// Returns diagnostics from directive parsing. +func (s *Scanner) ProcessFiles(fset *token.FileSet, files ...*ast.File) []analysis.Diagnostic { + return s.parser.ProcessFiles(fset, files...) +} + +// Lookup returns the directives at the given source position. +// If the file is not in cache, triggers FileParser.ProcessFilename to parse it. +func (s *Scanner) Lookup(fset *token.FileSet, pos token.Position) (Directives, []analysis.Diagnostic) { + if pos.Filename == "" { + return nil, nil + } + + if fd, ok := s.cache.Get(pos.Filename); ok { + return fd[pos.Line], nil + } + + // Cache miss - parse file (triggers onFileParsed callback, which stores + // the result via cache.Set and increments the miss counter). + diags := s.parser.ProcessFilename(fset, pos.Filename) + + // Peek avoids counting this self-induced read as a hit — the miss was + // already recorded by Set above. + if fd, ok := s.cache.Peek(pos.Filename); ok { + return fd[pos.Line], diags + } + + // Still not in cache means parsing failed. + return nil, diags +} + +func (s *Scanner) Stats() (hits, misses, size uint64) { + return s.cache.Stats() +} + +// fileDirectives holds directives found in a single file, indexed by line number. +type fileDirectives map[int]Directives + +// parseFileDirectives parses an AST file and extracts all exhaustruct directives. +// Returns diagnostics in case file parsing errors, directive parsing errors, or +// conflicting directives for the same target line. +func (*Scanner) parseFileDirectives(fset *token.FileSet, file *ast.File) (fileDirectives, []analysis.Diagnostic) { + directives, diagnostics := parseCommentDirectives(fset, file.Comments) + + if len(directives) == 0 { + return nil, diagnostics + } + + ast.Inspect(file, func(n ast.Node) bool { + switch n.(type) { + case nil, *ast.Comment, *ast.CommentGroup: + return false + } + + line := fset.Position(n.Pos()).Line + if d, ok := directives[line]; ok { + d.targetLine = line + directives[line] = d + } + + return true + }) + + result := make(fileDirectives, len(directives)) + + for line, d := range directives { + _, exists := result[d.targetLine] + if !exists { + result[d.targetLine] = d.directives + continue + } + + pos := d.pos + + // directives from block comments win over inline comments + if line != d.targetLine { + result[d.targetLine] = d.directives + pos = directives[d.targetLine].pos + } + + diagnostics = append(diagnostics, analysis.Diagnostic{ + Pos: pos, + Message: "directive ignored, conflicting directive already exists for the same target line", + }) + } + + return result, diagnostics +} + +type parsedDirective struct { + pos token.Pos + targetLine int + directives Directives +} + +func parseCommentDirectives( + fset *token.FileSet, + comments []*ast.CommentGroup, +) (map[int]parsedDirective, []analysis.Diagnostic) { + var ( + directives = make(map[int]parsedDirective) + diagnostics []analysis.Diagnostic + ) + + for _, cg := range comments { + hasDirective := false + + for _, comment := range cg.List { + found, parsed, errs := Parse(comment.Text) + if !found { + continue + } + + pos := comment.Pos() + + for _, err := range errs { + diagnostics = append(diagnostics, analysis.Diagnostic{ + Pos: pos, + Message: err.Error(), + }) + } + + if len(parsed) == 0 { + continue + } + + if hasDirective { + diagnostics = append(diagnostics, analysis.Diagnostic{ + Pos: pos, + Message: "multiple exhaustruct directives in a single comment group, ignoring", + }) + + continue + } + + hasDirective = true + directives[fset.Position(pos).Line] = parsedDirective{ + pos: pos, + targetLine: fset.Position(cg.End()).Line + 1, + directives: parsed, + } + } + } + + return directives, diagnostics +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/pattern/list.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/pattern/list.go new file mode 100644 index 00000000..c715839f --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/pattern/list.go @@ -0,0 +1,63 @@ +package pattern + +import ( + "regexp" + + "dev.gaijin.team/go/golib/e" + "dev.gaijin.team/go/golib/fields" +) + +// List is a collection of compiled regular expressions. +type List []*regexp.Regexp + +// NewList compiles patterns into a List. +// Returns error if any pattern is empty or invalid. +func NewList(patterns ...string) (List, error) { + if len(patterns) == 0 { + return nil, nil + } + + list := make(List, 0, len(patterns)) + + for _, pattern := range patterns { + re, err := compilePattern(pattern) + if err != nil { + return nil, err + } + + list = append(list, re) + } + + return list, nil +} + +// MatchFullString returns true if any regex matches the entire string. +// Pattern "test" matches "test" but not "testing" or "contest". +func (l List) MatchFullString(target string) bool { + if len(l) == 0 { + return false + } + + for i := range len(l) { + // A match spanning [0, len(target)) covers every byte of target, so the + // matched substring is target itself — no need to allocate it for comparison. + if loc := l[i].FindStringIndex(target); loc != nil && loc[0] == 0 && loc[1] == len(target) { + return true + } + } + + return false +} + +func compilePattern(pattern string) (*regexp.Regexp, error) { + if pattern == "" { + return nil, e.New("empty regular expression is not allowed") + } + + re, err := regexp.Compile(pattern) + if err != nil { + return nil, e.NewFrom("compile regular expression", err, fields.F("pattern", pattern)) + } + + return re, nil +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/structure/origin-scanner.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/structure/origin-scanner.go new file mode 100644 index 00000000..4f8c774d --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/structure/origin-scanner.go @@ -0,0 +1,121 @@ +package structure + +import ( + "go/ast" + "go/token" + + "golang.org/x/tools/go/analysis" + + "dev.gaijin.team/go/exhaustruct/v5/internal/astutil" + "dev.gaijin.team/go/exhaustruct/v5/internal/cache" +) + +type TypeOrigin uint8 + +const ( + OriginUnknown TypeOrigin = iota + OriginStruct + OriginAlias + OriginDerived +) + +type fileOrigins map[string]TypeOrigin + +// OriginScanner extracts type origin information from AST files. +// Registers with FileParser to process files as they are parsed. +// Thread-safe. +type OriginScanner struct { + parser *astutil.FileParser + cache *cache.Cache[string, fileOrigins] +} + +const originCachePrealloc = 64 + +func NewOriginScanner(parser *astutil.FileParser) *OriginScanner { + o := &OriginScanner{ + parser: parser, + cache: cache.New[string, fileOrigins](originCachePrealloc), + } + + parser.OnFileParsed(o.onFileParsed) + + return o +} + +func (o *OriginScanner) onFileParsed( + fset *token.FileSet, + file *ast.File, +) []analysis.Diagnostic { + filename := fset.Position(file.Pos()).Filename + + origins := extractTypeOrigins(file) + + o.cache.Set(filename, origins) + + return nil +} + +// Lookup returns the type origin for a named type in the given file. +// Triggers on-demand parsing if file is not cached. +func (o *OriginScanner) Lookup( + fset *token.FileSet, + filename string, + typeName string, +) TypeOrigin { + if filename == "" || typeName == "" { + return OriginUnknown + } + + if origins, ok := o.cache.Get(filename); ok { + return origins[typeName] + } + + // Cache miss - parse file (onFileParsed stores the result via cache.Set + // and records the miss). Peek avoids inflating the hit rate by re-reading + // the entry we just wrote. + o.parser.ProcessFilename(fset, filename) + + if origins, ok := o.cache.Peek(filename); ok { + return origins[typeName] + } + + return OriginUnknown +} + +func (o *OriginScanner) Stats() (hits, misses, size uint64) { + return o.cache.Stats() +} + +func extractTypeOrigins(file *ast.File) fileOrigins { + origins := make(fileOrigins) + + for _, decl := range file.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.TYPE { + continue + } + + for _, spec := range gd.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + + origins[ts.Name.Name] = classifyTypeSpec(ts) + } + } + + return origins +} + +func classifyTypeSpec(ts *ast.TypeSpec) TypeOrigin { + if ts.Assign != token.NoPos { + return OriginAlias + } + + if _, isStruct := ts.Type.(*ast.StructType); isStruct { + return OriginStruct + } + + return OriginDerived +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/structure/processor.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/structure/processor.go new file mode 100644 index 00000000..1c63030e --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/structure/processor.go @@ -0,0 +1,254 @@ +package structure + +import ( + "go/token" + "go/types" + + "golang.org/x/tools/go/analysis" + + "dev.gaijin.team/go/exhaustruct/v5/internal/cache" + "dev.gaijin.team/go/exhaustruct/v5/internal/directive" + "dev.gaijin.team/go/exhaustruct/v5/internal/pattern" +) + +type Processor struct { + directives *directive.Scanner + origins *OriginScanner + fieldsCache *cache.Cache[*types.Struct, structFields] + structCache *cache.Cache[token.Position, *Struct] + + enforce pattern.List `exhaustruct:"optional"` + ignore pattern.List `exhaustruct:"optional"` + optional pattern.List `exhaustruct:"optional"` + allowEmpty pattern.List `exhaustruct:"optional"` +} + +type Option func(*Processor) + +func WithEnforce(patterns pattern.List) Option { + return func(p *Processor) { p.enforce = patterns } +} + +func WithIgnore(patterns pattern.List) Option { + return func(p *Processor) { p.ignore = patterns } +} + +func WithOptional(patterns pattern.List) Option { + return func(p *Processor) { p.optional = patterns } +} + +func WithAllowEmpty(patterns pattern.List) Option { + return func(p *Processor) { p.allowEmpty = patterns } +} + +const cachePreallocSize = 64 + +func NewProcessor(directives *directive.Scanner, origins *OriginScanner, opts ...Option) *Processor { + p := &Processor{ + directives: directives, + origins: origins, + fieldsCache: cache.New[*types.Struct, structFields](cachePreallocSize), + structCache: cache.New[token.Position, *Struct](cachePreallocSize), + } + + for _, opt := range opts { + opt(p) + } + + return p +} + +// Directives returns the directive scanner the processor was constructed +// with, shared so callers can resolve use-site directives against the same +// cache. +func (p *Processor) Directives() *directive.Scanner { + return p.directives +} + +// ResolveStruct returns Struct metadata for the given type. +// Type resolution (pointers, aliases) is done by the caller. +// +// Parameters: +// - typeName: the type's TypeName, or nil for anonymous structs +// - strct: the underlying struct type (required) +// - pos: position of type definition (from analyzer's AST inspection) +// - callerPkg: package context, used for anonymous struct path +func (p *Processor) ResolveStruct( + fset *token.FileSet, + typeName *types.TypeName, + strct *types.Struct, + pos token.Pos, + callerPkg *types.Package, +) (*Struct, []analysis.Diagnostic) { + if strct == nil { + return nil, nil + } + + position := fset.Position(pos) + + // Check cache before allocating + if position.IsValid() { + if cached, ok := p.structCache.Get(position); ok { + return cached, nil + } + } + + s := p.buildStruct(typeName, position, callerPkg) + + diags := p.populateFields(fset, s, strct) + p.resolveStructOrigin(fset, s) + + diags = append(diags, p.resolveStructDirectives(fset, s)...) + p.matchStructPatterns(s) + + if s.Position.IsValid() { + p.structCache.Set(s.Position, s) + } + + return s, diags +} + +// buildStruct creates Struct metadata from type info. +func (*Processor) buildStruct(typeName *types.TypeName, pos token.Position, callerPkg *types.Package) *Struct { + if typeName != nil { + pkg := typeName.Pkg() + + return &Struct{ + Name: typeName.Name(), + FullPath: pkg.Path() + "." + typeName.Name(), + PackageName: pkg.Name(), + Position: pos, + } + } + + // Anonymous struct + return &Struct{ + Name: AnonymousName, + FullPath: callerPkg.Path() + "." + AnonymousName, + PackageName: callerPkg.Name(), + Position: pos, + } +} + +func (p *Processor) getStructFields(fset *token.FileSet, strct *types.Struct) (structFields, []analysis.Diagnostic) { + if fields, ok := p.fieldsCache.Get(strct); ok { + return fields, nil + } + + fields, diags := p.resolveStructFields(fset, strct) + + p.fieldsCache.Set(strct, fields) + + return fields, diags +} + +func (p *Processor) resolveStructFields( + fset *token.FileSet, + strct *types.Struct, +) (structFields, []analysis.Diagnostic) { + result := structFields{ + packagePath: "", + fields: make([]fieldInfo, 0, strct.NumFields()), + } + + var diags []analysis.Diagnostic + + for f := range strct.Fields() { + if result.packagePath == "" && f.Pkg() != nil { + result.packagePath = f.Pkg().Path() + } + + field := fieldInfo{ + name: f.Name(), + exported: f.Exported(), + } + + if p.directives != nil { + fieldPos := fset.Position(f.Pos()) + dirs, d := p.directives.Lookup(fset, fieldPos) + + diags = append(diags, d...) + + field.enforced = dirs.Contains(directive.Enforce) + field.optional = dirs.Contains(directive.Optional) + } + + result.fields = append(result.fields, field) + } + + return result, diags +} + +func (p *Processor) populateFields(fset *token.FileSet, s *Struct, strct *types.Struct) []analysis.Diagnostic { + resolved, diags := p.getStructFields(fset, strct) + + // Fields are external when declared in a different package than the struct type. + // This happens for derived types and aliases from external packages. + // + // Rationale behind that filtering is that noone except package that has declared + // the struct can access unexported fields, therefore we can simply filter them + // out to save up on storage. Usage of derived type from the package of structure + // definition is simply impossible since it will cause import cycle - thus, such + // filtering is safe. + fieldsExternal := resolved.packagePath != s.PackagePath() + + s.Fields = Fields{ + PackagePath: resolved.packagePath, + Items: make([]Field, 0, len(resolved.fields)), + } + + for _, sf := range resolved.fields { + if fieldsExternal && !sf.exported { + continue + } + + fieldPath := s.FullPath + "#" + sf.name + + s.Fields.Items = append(s.Fields.Items, Field{ + Name: sf.name, + Exported: sf.exported, + Enforced: sf.enforced, + Optional: sf.optional, + PatternEnforced: p.enforce.MatchFullString(fieldPath), + PatternOptional: p.optional.MatchFullString(fieldPath), + }) + } + + return diags +} + +func (p *Processor) resolveStructOrigin(fset *token.FileSet, s *Struct) { + if !s.Position.IsValid() || s.Name == AnonymousName { + return + } + + origin := p.origins.Lookup(fset, s.Position.Filename, s.Name) + + s.IsAlias = origin == OriginAlias + s.IsDerived = origin == OriginDerived +} + +func (p *Processor) resolveStructDirectives(fset *token.FileSet, s *Struct) []analysis.Diagnostic { + if p.directives == nil || !s.Position.IsValid() { + return nil + } + + dirs, diags := p.directives.Lookup(fset, s.Position) + + s.Enforced = dirs.Contains(directive.Enforce) + s.Ignored = dirs.Contains(directive.Ignore) + s.Optional = dirs.Contains(directive.Optional) + + return diags +} + +func (p *Processor) matchStructPatterns(s *Struct) { + s.PatternEnforced = p.enforce.MatchFullString(s.FullPath) + s.PatternIgnored = p.ignore.MatchFullString(s.FullPath) + s.PatternOptional = p.optional.MatchFullString(s.FullPath) + s.AllowEmptyDecl = p.allowEmpty.MatchFullString(s.FullPath) +} + +func (p *Processor) Stats() (hits, misses, size uint64) { + return p.structCache.Stats() +} diff --git a/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/structure/struct.go b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/structure/struct.go new file mode 100644 index 00000000..c6cd49f2 --- /dev/null +++ b/vendor/dev.gaijin.team/go/exhaustruct/v5/internal/structure/struct.go @@ -0,0 +1,224 @@ +package structure + +import ( + "go/ast" + "go/token" + "strings" +) + +const AnonymousName = "" + +// fieldInfo contains raw field data independent of type name. +type fieldInfo struct { + // name is the name of the field. + name string + // exported indicates if the field is exported. + exported bool `exhaustruct:"optional"` + // enforced indicates if the field is enforced via directive. + enforced bool `exhaustruct:"optional"` + // optional indicates if the field is optional via directive. + optional bool `exhaustruct:"optional"` +} + +// structFields contains field information for a struct, independent of type name. +type structFields struct { + // packagePath is the package path where fields are declared. + packagePath string + // fields is the list of fields in declaration order. + fields []fieldInfo +} + +type Struct struct { + Name string + FullPath string + PackageName string + + Position token.Position `exhaustruct:"optional"` + Fields Fields `exhaustruct:"optional"` + + Enforced bool `exhaustruct:"optional"` + Ignored bool `exhaustruct:"optional"` + Optional bool `exhaustruct:"optional"` + + PatternEnforced bool `exhaustruct:"optional"` + PatternIgnored bool `exhaustruct:"optional"` + PatternOptional bool `exhaustruct:"optional"` + + AllowEmptyDecl bool `exhaustruct:"optional"` + + // Detected via OriginScanner AST inspection before types.Unalias. + IsAlias bool `exhaustruct:"optional"` + IsDerived bool `exhaustruct:"optional"` +} + +// PackagePath returns the package path of the struct type. +func (s *Struct) PackagePath() string { + if idx := strings.LastIndex(s.FullPath, "."); idx >= 0 { + return s.FullPath[:idx] + } + + return s.FullPath +} + +// IsEnforced returns true if struct is enforced via directive or pattern. +func (s *Struct) IsEnforced() bool { + return s.Enforced || s.PatternEnforced +} + +// IsIgnored returns true if struct is ignored via directive or pattern. +func (s *Struct) IsIgnored() bool { + return s.Ignored || s.PatternIgnored +} + +// IsOptional returns true if struct is optional via directive or pattern. +func (s *Struct) IsOptional() bool { + return s.Optional || s.PatternOptional +} + +// SkippedFields returns missing required fields for a composite literal. +// callerPkgPath is used to determine if unexported fields are accessible. +// For positional literals: returns fields after the last provided element. +// For named literals: returns fields not present in the literal. +func (s *Struct) SkippedFields(lit *ast.CompositeLit, callerPkgPath string) []Field { + externalPkg := s.Fields.PackagePath != callerPkgPath + + if isNamedLiteral(lit) { + return s.skippedNamed(lit, externalPkg) + } + + return s.skippedPositional(len(lit.Elts), externalPkg) +} + +// isNamedLiteral checks if a composite literal uses named fields. It treats +// empty literals as not named, since positional literals checks are simpler. +func isNamedLiteral(lit *ast.CompositeLit) bool { + if len(lit.Elts) == 0 { + return false + } + + _, ok := lit.Elts[0].(*ast.KeyValueExpr) + + return ok +} + +func (s *Struct) skippedPositional(count int, externalPkg bool) []Field { + items := s.Fields.Items + + if count >= len(items) { + return nil + } + + remaining := items[count:] + missing := make([]Field, 0, len(remaining)) + + for _, f := range remaining { + if s.isFieldRequired(f, externalPkg) { + missing = append(missing, f) + } + } + + if len(missing) == 0 { + return nil + } + + return missing +} + +func (s *Struct) skippedNamed(lit *ast.CompositeLit, externalPkg bool) []Field { + present := make(map[string]bool, len(lit.Elts)) + + for _, elt := range lit.Elts { + if kv, ok := elt.(*ast.KeyValueExpr); ok { + if k, ok := kv.Key.(*ast.Ident); ok { + present[k.Name] = true + } + } + } + + missing := make([]Field, 0, len(s.Fields.Items)-len(present)) + + for _, f := range s.Fields.Items { + if !present[f.Name] && s.isFieldRequired(f, externalPkg) { + missing = append(missing, f) + } + } + + if len(missing) == 0 { + return nil + } + + return missing +} + +func (s *Struct) isFieldRequired(f Field, externalPkg bool) bool { + // explicit field directives win over everything + if f.Enforced { + return true + } + + if f.Optional { + return false + } + + // field-level patterns apply only when they specifically target the field — + // i.e., the pattern matches the field path but not the struct path. A broad + // pattern that also matches the struct is handled via s.IsEnforced/IsOptional + // and must not silently promote unrelated fields to required/optional. + if f.PatternEnforced && !s.PatternEnforced { + return true + } + + if f.PatternOptional && !s.PatternOptional { + return false + } + + // optionality can be inherited from the structure settings + if s.IsOptional() { + return false + } + + // unexported fields are only required for same-package usage + if externalPkg && !f.Exported { + return false + } + + return true +} + +type Field struct { + Name string + Exported bool `exhaustruct:"optional"` + Enforced bool `exhaustruct:"optional"` + Optional bool `exhaustruct:"optional"` + + PatternEnforced bool `exhaustruct:"optional"` + PatternOptional bool `exhaustruct:"optional"` +} + +// Fields is a collection of struct fields with shared package metadata. +// Items are in declaration order (required for positional literals). +type Fields struct { + PackagePath string + Items []Field +} + +func FormatFieldNames(fields []Field) string { + switch len(fields) { + case 0: + return "" + case 1: + return fields[0].Name + } + + var b strings.Builder + + b.Grow(len(fields)) + b.WriteString(fields[0].Name) + + for _, s := range fields[1:] { + b.WriteString(", ") + b.WriteString(s.Name) + } + + return b.String() +} diff --git a/vendor/dev.gaijin.team/go/golib/fields/field.go b/vendor/dev.gaijin.team/go/golib/fields/field.go index 324b2970..716586ba 100644 --- a/vendor/dev.gaijin.team/go/golib/fields/field.go +++ b/vendor/dev.gaijin.team/go/golib/fields/field.go @@ -1,4 +1,3 @@ -// Package fields provides types and functions for working with key-value fields. package fields import ( diff --git a/vendor/dev.gaijin.team/go/golib/fields/list.go b/vendor/dev.gaijin.team/go/golib/fields/list.go index 5a881f77..7da83b20 100644 --- a/vendor/dev.gaijin.team/go/golib/fields/list.go +++ b/vendor/dev.gaijin.team/go/golib/fields/list.go @@ -44,7 +44,7 @@ func (l List) ToDict() Dict { // } func (l List) All() iter.Seq2[string, any] { return func(yield func(string, any) bool) { - for i := 0; i < len(l); i++ { + for i := range len(l) { if !yield(l[i].K, l[i].V) { return } diff --git a/vendor/github.com/Abirdcfly/dupword/dupword.go b/vendor/github.com/Abirdcfly/dupword/dupword.go index a3eaced8..5b482006 100644 --- a/vendor/github.com/Abirdcfly/dupword/dupword.go +++ b/vendor/github.com/Abirdcfly/dupword/dupword.go @@ -99,15 +99,17 @@ func NewAnalyzer() *analysis.Analyzer { a.Flags.Var(&analyzer.keywords, "keyword", "keywords for detecting duplicate words") a.Flags.Var(&analyzer.ignoreWords, "ignore", "ignore words") a.Flags.BoolVar(&analyzer.commentsOnly, "comments-only", false, "check only comments, skip strings") + a.Flags.BoolVar(&analyzer.skipRawStrings, "skip-raw-strings", false, "skip raw string literals (backtick-delimited)") a.Flags.Var(version{}, "V", "print version and exit") return a } type analyzer struct { - keywords keywords - ignoreWords ignore - commentsOnly bool + keywords keywords + ignoreWords ignore + commentsOnly bool + skipRawStrings bool } func (a *analyzer) run(pass *analysis.Pass) (interface{}, error) { @@ -186,6 +188,9 @@ func (a *analyzer) fixDuplicateWordInString(pass *analysis.Pass, lit *ast.BasicL if lit.Kind != token.STRING { return } + if a.skipRawStrings && len(lit.Value) > 0 && lit.Value[0] == '`' { + return + } value, err := strconv.Unquote(lit.Value) if err != nil { fmt.Printf("lit.Value:%v, err: %v\n", lit.Value, err) @@ -193,9 +198,14 @@ func (a *analyzer) fixDuplicateWordInString(pass *analysis.Pass, lit *ast.BasicL value = lit.Value } quote := value != lit.Value + isRawString := quote && len(lit.Value) > 0 && lit.Value[0] == '`' update, keyword, find := a.Check(value) if quote { - update = strconv.Quote(update) + if isRawString { + update = "`" + update + "`" + } else { + update = strconv.Quote(update) + } } if find { pass.Report(analysis.Diagnostic{Pos: lit.Pos(), End: lit.End(), Message: fmt.Sprintf(Message, keyword), SuggestedFixes: []analysis.SuggestedFix{{ @@ -268,8 +278,8 @@ func (a *analyzer) checkOneKey(raw, key string) (new string, findWord string, fi // space start position spaceStart = i curWord = raw[wordStart:i] - } else if i == len(raw)-1 { - // last position + } else if i == len(raw)-1 && !unicode.IsSpace(r) { + // last position (non-space): write the final word word := raw[wordStart:] if ((key != "" && word == key) || key == "") && word == preWord { if !a.excludeWords(cutTrailingCommas(word)) { @@ -283,6 +293,25 @@ func (a *analyzer) checkOneKey(raw, key string) (new string, findWord string, fi } lastRune = r } + // Flush remaining content when the string ends with whitespace: + // the last word (curWord) and trailing whitespace haven't been written yet. + if len(raw) > 0 && unicode.IsSpace(rune(raw[len(raw)-1])) { + if curWord != "" { + if ((key != "" && curWord == key) || key == "") && curWord == preWord && curWord != "" { + if !a.excludeWords(cutTrailingCommas(curWord)) { + find = true + findWordMap[curWord] = true + } else { + newLine.WriteString(lastSpace) + newLine.WriteString(curWord) + } + } else { + newLine.WriteString(lastSpace) + newLine.WriteString(curWord) + } + } + newLine.WriteString(raw[spaceStart:]) + } if find { new = newLine.String() findWordSlice := make([]string, len(findWordMap)) diff --git a/vendor/github.com/AlwxSin/noinlineerr/noinlineerr.go b/vendor/github.com/AlwxSin/noinlineerr/noinlineerr.go index 9bf56232..91d3c6bd 100644 --- a/vendor/github.com/AlwxSin/noinlineerr/noinlineerr.go +++ b/vendor/github.com/AlwxSin/noinlineerr/noinlineerr.go @@ -4,6 +4,7 @@ import ( "bytes" "go/ast" "go/printer" + "go/token" "go/types" "golang.org/x/tools/go/analysis" @@ -11,7 +12,13 @@ import ( "golang.org/x/tools/go/ast/inspector" ) -const errMessage = "avoid inline error handling using `if err := ...; err != nil`; use plain assignment `err := ...`" +func errMessage(tok token.Token) string { + if tok == token.ASSIGN { + return "avoid inline error handling using `if err = ...; err != nil`; use plain assignment `err = ...`" + } + + return "avoid inline error handling using `if err := ...; err != nil`; use plain assignment `err := ...`" +} func NewAnalyzer() *analysis.Analyzer { return &analysis.Analyzer{ @@ -67,8 +74,8 @@ func inlineErrorInspector(pass *analysis.Pass) func(n ast.Node) { // or there are any variables with same name // then we can make a shadow conflict with other variables // so don't do anything beside simple error message - if len(assignStmt.Lhs) != 1 || shadowVarsExists(ident.Name, pass.TypesInfo.Scopes[ifStmt]) { - pass.Reportf(ident.Pos(), errMessage) + if len(assignStmt.Lhs) != 1 || (assignStmt.Tok == token.DEFINE && shadowVarsExists(ident.Name, pass.TypesInfo.Scopes[ifStmt])) { + pass.Reportf(ident.Pos(), errMessage(assignStmt.Tok)) return } @@ -85,13 +92,13 @@ func inlineErrorInspector(pass *analysis.Pass) func(n ast.Node) { pass.Report(analysis.Diagnostic{ Pos: ident.Pos(), End: ident.End(), - Message: errMessage, + Message: errMessage(assignStmt.Tok), SuggestedFixes: []analysis.SuggestedFix{ { Message: "move err assignment outside if", TextEdits: []analysis.TextEdit{ { - // insert err := ... before if + // insert err := ... or err = ... before if Pos: ifStmt.Pos(), End: ifStmt.Pos(), NewText: []byte(assignText + "\n"), diff --git a/vendor/github.com/ClickHouse/clickhouse-go-linter/LICENSE b/vendor/github.com/ClickHouse/clickhouse-go-linter/LICENSE new file mode 100644 index 00000000..a7e77cb2 --- /dev/null +++ b/vendor/github.com/ClickHouse/clickhouse-go-linter/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/vendor/github.com/ClickHouse/clickhouse-go-linter/internal/util/util.go b/vendor/github.com/ClickHouse/clickhouse-go-linter/internal/util/util.go new file mode 100644 index 00000000..68d13098 --- /dev/null +++ b/vendor/github.com/ClickHouse/clickhouse-go-linter/internal/util/util.go @@ -0,0 +1,33 @@ +package util + +import ( + "go/ast" + "go/types" + + "golang.org/x/tools/go/analysis" +) + +// IsChObj returns true if the input ast.Expr is of type clickhouse go driver +func IsChObj(pass *analysis.Pass, expr ast.Expr, name string) bool { + t := pass.TypesInfo.TypeOf(expr) + if t == nil { + return false + } + named, ok := t.(*types.Named) + if !ok { + return false + } + obj := named.Obj() + if obj.Pkg() == nil { + return false + } + return obj.Pkg().Path() == "github.com/ClickHouse/clickhouse-go/v2/lib/driver" && + obj.Name() == name +} + +func IdentName(expr ast.Expr) string { + if id, ok := expr.(*ast.Ident); ok { + return id.Name + } + return "" +} diff --git a/vendor/github.com/ClickHouse/clickhouse-go-linter/passes/chbatchclose/chbatchclose.go b/vendor/github.com/ClickHouse/clickhouse-go-linter/passes/chbatchclose/chbatchclose.go new file mode 100644 index 00000000..cc0f6894 --- /dev/null +++ b/vendor/github.com/ClickHouse/clickhouse-go-linter/passes/chbatchclose/chbatchclose.go @@ -0,0 +1,227 @@ +package chbatchclose + +import ( + "go/ast" + "go/token" + "os" + "strconv" + + "github.com/ClickHouse/clickhouse-go-linter/internal/util" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +type analyzer struct { + // if true, report valid usages and log spurious but valid cases. + debug bool +} + +func NewAnalyzer() *analysis.Analyzer { + debug, _ := strconv.ParseBool(os.Getenv("CH_GO_LINTER_DEBUG")) + a := analyzer{ + debug: debug, + } + return &analysis.Analyzer{ + Name: "chbatchclosecheck", + Doc: "chbatchclosecheck checks whether defer batch.Close() is called on ClickHouse driver Batch variables", + Run: a.run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + } +} + +func (a *analyzer) run(pass *analysis.Pass) (any, error) { + insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + nodeFilter := []ast.Node{ + (*ast.FuncDecl)(nil), + (*ast.FuncLit)(nil), + } + + insp.Preorder(nodeFilter, func(n ast.Node) { + var body *ast.BlockStmt + switch fn := n.(type) { + case *ast.FuncDecl: + body = fn.Body + case *ast.FuncLit: + body = fn.Body + } + + if body == nil { + return + } + a.checkFunc(pass, body) + }) + + return nil, nil +} + +// batchUsage tracks whether a driver.Batch variable has a defer Close/Abort or is returned. +type batchUsage struct { + assignPos token.Pos + deferredClose bool + returned bool +} + +func (b *batchUsage) report(varName string, pass *analysis.Pass, debug bool) { + if b.assignPos == token.NoPos { + // no usage of Batch + return + } + if !b.deferredClose && !b.returned { + pass.Reportf(b.assignPos, + "clickhouse Batch %s must be closed defensively with defer %s.Close() after successful instantiation", + varName, varName) + } else if debug { + if b.deferredClose { + pass.Reportf(b.assignPos, + "clickhouse Batch %s is properly closed defensively after successful instantiation [valid]", + varName) + } else { + pass.Reportf(b.assignPos, + "clickhouse Batch %s is returned by the function [valid]", + varName) + } + } +} + +// checkFunc analyzes a single function/closure body. +// It does a single-pass collection of Batch assignments, defer Close/Abort calls, and return statements. +// It does not descend into nested closures (they are handled as separate units by the Preorder visitor above). +func (a *analyzer) checkFunc(pass *analysis.Pass, body *ast.BlockStmt) { + usages := map[string]*batchUsage{} + + ast.Inspect(body, func(n ast.Node) bool { + if n == nil { + return false + } + // don't descend into nested closures + if n != body { + if _, ok := n.(*ast.FuncLit); ok { + return false + } + } + + switch node := n.(type) { + case *ast.AssignStmt: + a.handleAssign(pass, node, usages) + case *ast.DeferStmt: + handleDefer(node, usages) + case *ast.ReturnStmt: + handleReturn(node, usages) + } + + return true + }) + + // remaining usages that were not flushed + for varName, u := range usages { + u.report(varName, pass, a.debug) + } +} + +// handleAssign checks if any LHS variable in the assignment is of type driver.Batch. +// If a tracked variable is reassigned, it flushes/reports the previous tracking first. +func (a *analyzer) handleAssign(pass *analysis.Pass, assign *ast.AssignStmt, usages map[string]*batchUsage) { + for _, lhs := range assign.Lhs { + name := util.IdentName(lhs) + if name == "" { + continue + } + if name == "_" && util.IsChObj(pass, lhs, "Batch") { + pass.Reportf(assign.Pos(), "clickhouse Batch assigned to blank identifier. Connection leak. clickhouse Batch must be instantiated and closed defensively with defer batch.Close() after successful instantiation") + continue + } + + // if this var was already tracked, flush previous usage before re-tracking + if u, ok := usages[name]; ok { + u.report(name, pass, a.debug) + delete(usages, name) + } + + if util.IsChObj(pass, lhs, "Batch") { + usages[name] = &batchUsage{assignPos: assign.Pos()} + } + } +} + +// handleDefer checks if a defer statement calls Close() on a tracked Batch variable. +// Two shapes are supported: +// - direct selector call: defer batch.Close() +// - immediately-invoked closure: defer func() { ... batch.Close() ... }() +// (no params; nested FuncLits inside the closure body are not descended into) +func handleDefer(deferStmt *ast.DeferStmt, usages map[string]*batchUsage) { + call := deferStmt.Call + + switch fun := call.Fun.(type) { + case *ast.SelectorExpr: + varName := util.IdentName(fun.X) + if varName == "" { + return + } + u, exists := usages[varName] + if !exists { + return + } + if fun.Sel.Name == "Close" { + u.deferredClose = true + } + case *ast.FuncLit: + // only no-arg closures are supported for the moment + if fun.Type.Params != nil && len(fun.Type.Params.List) > 0 { + return + } + handleDeferredClosure(fun.Body, usages) + } +} + +// handleDeferredClosure walks the body of a deferred closure looking for +// `.Close()` calls. It does not descend into nested FuncLits, so +// `defer func() { go func() { batch.Close() }() }()` and similar are not +// treated as a valid defensive close. +func handleDeferredClosure(body *ast.BlockStmt, usages map[string]*batchUsage) { + ast.Inspect(body, func(n ast.Node) bool { + if n == nil { + return false + } + // don't descend into nested closures (goroutines, callbacks, etc.) + if n != body { + if _, ok := n.(*ast.FuncLit); ok { + return false + } + } + call, ok := n.(*ast.CallExpr) + if !ok { + return true + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return true + } + if sel.Sel.Name != "Close" { + return true + } + varName := util.IdentName(sel.X) + if varName == "" { + return true + } + if u, exists := usages[varName]; exists { + u.deferredClose = true + } + return true + }) +} + +// handleReturn checks if any return value is a tracked Batch variable. +func handleReturn(ret *ast.ReturnStmt, usages map[string]*batchUsage) { + for _, result := range ret.Results { + name := util.IdentName(result) + if name == "" { + continue + } + if u, exists := usages[name]; exists { + u.returned = true + } + } +} diff --git a/vendor/github.com/ClickHouse/clickhouse-go-linter/passes/chrowserr/chrowserr.go b/vendor/github.com/ClickHouse/clickhouse-go-linter/passes/chrowserr/chrowserr.go new file mode 100644 index 00000000..bba0f82a --- /dev/null +++ b/vendor/github.com/ClickHouse/clickhouse-go-linter/passes/chrowserr/chrowserr.go @@ -0,0 +1,162 @@ +package chrowserr + +import ( + "go/ast" + "go/token" + "log" + "os" + "strconv" + + "github.com/ClickHouse/clickhouse-go-linter/internal/util" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +type analyzer struct { + // if true, report valid usages and log spurious but valid cases. + debug bool +} + +func NewAnalyzer() *analysis.Analyzer { + debug, _ := strconv.ParseBool(os.Getenv("CH_GO_LINTER_DEBUG")) + a := analyzer{ + debug: debug, + } + return &analysis.Analyzer{ + Name: "chrowserrcheck", + Doc: "chrowserrcheck checks whether ClickHouse driver Rows.Err is called after Rows.Next()", + Run: a.run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + } +} + +func (a *analyzer) run(pass *analysis.Pass) (any, error) { + insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + nodeFilter := []ast.Node{ + (*ast.FuncDecl)(nil), + (*ast.FuncLit)(nil), + } + + insp.Preorder(nodeFilter, func(n ast.Node) { + var body *ast.BlockStmt + switch fn := n.(type) { + case *ast.FuncDecl: + body = fn.Body + case *ast.FuncLit: + body = fn.Body + } + + if body == nil { + return + } + a.checkFunc(pass, body) + }) + + return nil, nil +} + +// rowsUsage tracks Next/Err usage observations for a single Rows variable within a function. +type rowsUsage struct { + nextPos token.Pos + errCalled bool +} + +func (r *rowsUsage) report(varName string, pass *analysis.Pass, debug bool) { + if r.nextPos == token.NoPos { + // no usage of rows.Next() + return + } + // rows.Next() was called + if !r.errCalled { + pass.Reportf(r.nextPos, + "clickhouse %s.Err() must be checked after %s.Next()", + varName, varName) + } else if debug { + // for dev purpose - list valid usages + pass.Reportf(r.nextPos, + "clickhouse %s.Err() is properly called after %s.Next() [valid]", + varName, varName) + } +} + +// checkFunc analyzes a single function/closure body. +// It does a single-pass collection of Next() and Err() calls, +// it does not descend into nested closures (they are handled as separate units by the Preorder visitor above). +func (a *analyzer) checkFunc(pass *analysis.Pass, body *ast.BlockStmt) { + usages := map[string]*rowsUsage{} + ast.Inspect(body, func(n ast.Node) bool { + if n == nil { + return false + } + // don't descend into nested closures + if n != body { + if _, ok := n.(*ast.FuncLit); ok { + return false + } + } + + // if a tracked var appears in an assignment, it means it is re-assigned - run lint report and flush var from usages. + if assign, ok := n.(*ast.AssignStmt); ok { + for _, lhs := range assign.Lhs { + name := util.IdentName(lhs) + if name == "" { + continue + } + if s, ok := usages[name]; ok { + s.report(name, pass, a.debug) + } + delete(usages, name) + } + // let ast.Inspect continue + return true + } + + // handle CH driver method calls Next() and Err() - skip everything else + call, ok := n.(*ast.CallExpr) + if !ok { + return true + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return true + } + varName := util.IdentName(sel.X) + if varName == "" { + return true + } + if !util.IsChObj(pass, sel.X, "Rows") { + return true + } + + // call is a CH driver Rows method call + switch sel.Sel.Name { + case "Next": + if _, exists := usages[varName]; !exists { + usages[varName] = &rowsUsage{nextPos: call.Pos()} + } else if a.debug { + log.Printf("Rows.Next() is written multiple times with no re-assignment. Valid but rare usage. If this observation is not correct, it is a bug in this linter library. Please reach out to maintainer.") + // in particular could be a bug in the re-assignment detection + } + case "Err": + if s, exists := usages[varName]; exists { + s.errCalled = true + s.report(varName, pass, a.debug) + delete(usages, varName) + } else if a.debug { + log.Printf("%s.Err() is called on ClickHouse Rows %s but %s.Next() was never called. Valid but unexpected usage. If this observation is not correct, it is a bug in this linter library. Please reach out to maintainer.", + varName, varName, varName) + // in particular could be a bug in the re-assignment detection + } + + } + + return true + }) + + // remaining usages that were not flushed (misusages only, as correct usages are flushed already) + for varName, s := range usages { + s.report(varName, pass, a.debug) + } +} diff --git a/vendor/github.com/Masterminds/semver/v3/.gitignore b/vendor/github.com/Masterminds/semver/v3/.gitignore index 6b061e61..35f0e5a3 100644 --- a/vendor/github.com/Masterminds/semver/v3/.gitignore +++ b/vendor/github.com/Masterminds/semver/v3/.gitignore @@ -1 +1,2 @@ -_fuzz/ \ No newline at end of file +_fuzz/ +.devcontainer/ \ No newline at end of file diff --git a/vendor/github.com/Masterminds/semver/v3/.golangci.yml b/vendor/github.com/Masterminds/semver/v3/.golangci.yml index fbc63325..24277f3a 100644 --- a/vendor/github.com/Masterminds/semver/v3/.golangci.yml +++ b/vendor/github.com/Masterminds/semver/v3/.golangci.yml @@ -1,27 +1,42 @@ -run: - deadline: 2m - +version: "2" linters: - disable-all: true + default: none enable: - - misspell - - govet - - staticcheck + - dupl - errcheck - - unparam + - gocyclo + - gosec + - govet - ineffassign + - misspell - nakedret - - gocyclo - - dupl - - goimports - revive - - gosec - - gosimple - - typecheck + - staticcheck + - unparam - unused - -linters-settings: - gofmt: - simplify: true - dupl: - threshold: 600 + settings: + dupl: + threshold: 600 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - goimports + settings: + gofmt: + simplify: true + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/vendor/github.com/Masterminds/semver/v3/constraints.go b/vendor/github.com/Masterminds/semver/v3/constraints.go index 8b7a10f8..e8353bc4 100644 --- a/vendor/github.com/Masterminds/semver/v3/constraints.go +++ b/vendor/github.com/Masterminds/semver/v3/constraints.go @@ -21,21 +21,43 @@ type Constraints struct { IncludePrerelease bool } +// MaxConstraintLen is the maximum allowed length of a constraint string. +const MaxConstraintLen = 512 + +// MaxConstraintGroups is the maximum number of OR groups allowed in a +// constraint string. +const MaxConstraintGroups = 32 + +// ErrConstraintTooLong is returned when a constraint string exceeds the +// maximum allowed length. +var ErrConstraintTooLong = fmt.Errorf("constraint string is too long (max %d bytes)", MaxConstraintLen) + +// ErrTooManyConstraintGroups is returned when a constraint string contains +// too many OR groups. +var ErrTooManyConstraintGroups = fmt.Errorf("too many constraint groups (max %d)", MaxConstraintGroups) + // NewConstraint returns a Constraints instance that a Version instance can // be checked against. If there is a parse error it will be returned. func NewConstraint(c string) (*Constraints, error) { + if len(c) > MaxConstraintLen { + return nil, ErrConstraintTooLong + } + // Rewrite - ranges into a comparison operation. c = rewriteRange(c) ors := strings.Split(c, "||") + if len(ors) > MaxConstraintGroups { + return nil, ErrTooManyConstraintGroups + } lenors := len(ors) or := make([][]*constraint, lenors) hasPre := make([]bool, lenors) for k, v := range ors { // Validate the segment if !validConstraintRegex.MatchString(v) { - return nil, fmt.Errorf("improper constraint: %s", v) + return nil, fmt.Errorf("improper constraint: %q", v) } cs := findConstraintRegex.FindAllString(v, -1) @@ -104,9 +126,9 @@ func (cs Constraints) Validate(v *Version) (bool, []error) { for _, c := range o { // Before running the check handle the case there the version is // a prerelease and the check is not searching for prereleases. - if !(cs.IncludePrerelease || cs.containsPre[i]) && v.pre != "" { + if !cs.IncludePrerelease && !cs.containsPre[i] && v.pre != "" { if !prerelesase { - em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + em := fmt.Errorf("%q is a prerelease version and the constraint is only looking for release versions", v) e = append(e, em) prerelesase = true } @@ -258,7 +280,7 @@ func parseConstraint(c string) (*constraint, error) { if len(c) > 0 { m := constraintRegex.FindStringSubmatch(c) if m == nil { - return nil, fmt.Errorf("improper constraint: %s", c) + return nil, fmt.Errorf("improper constraint: %q", c) } cs := &constraint{ @@ -325,7 +347,7 @@ func constraintNotEqual(v *Version, c *constraint, includePre bool) (bool, error // The existence of prereleases is checked at the group level and passed in. // Exit early if the version has a prerelease but those are to be ignored. if v.Prerelease() != "" && !includePre { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + return false, fmt.Errorf("%q is a prerelease version and the constraint is only looking for release versions", v) } if c.dirty { @@ -335,7 +357,7 @@ func constraintNotEqual(v *Version, c *constraint, includePre bool) (bool, error if c.con.Minor() != v.Minor() && !c.minorDirty { return true, nil } else if c.minorDirty { - return false, fmt.Errorf("%s is equal to %s", v, c.orig) + return false, fmt.Errorf("%q is equal to %q", v, c.orig) } else if c.con.Patch() != v.Patch() && !c.patchDirty { return true, nil } else if c.patchDirty { @@ -345,15 +367,15 @@ func constraintNotEqual(v *Version, c *constraint, includePre bool) (bool, error if eq { return true, nil } - return false, fmt.Errorf("%s is equal to %s", v, c.orig) + return false, fmt.Errorf("%q is equal to %q", v, c.orig) } - return false, fmt.Errorf("%s is equal to %s", v, c.orig) + return false, fmt.Errorf("%q is equal to %q", v, c.orig) } } eq := v.Equal(c.con) if eq { - return false, fmt.Errorf("%s is equal to %s", v, c.orig) + return false, fmt.Errorf("%q is equal to %q", v, c.orig) } return true, nil @@ -364,7 +386,7 @@ func constraintGreaterThan(v *Version, c *constraint, includePre bool) (bool, er // The existence of prereleases is checked at the group level and passed in. // Exit early if the version has a prerelease but those are to be ignored. if v.Prerelease() != "" && !includePre { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + return false, fmt.Errorf("%q is a prerelease version and the constraint is only looking for release versions", v) } var eq bool @@ -374,17 +396,17 @@ func constraintGreaterThan(v *Version, c *constraint, includePre bool) (bool, er if eq { return true, nil } - return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) + return false, fmt.Errorf("%q is less than or equal to %q", v, c.orig) } if v.Major() > c.con.Major() { return true, nil } else if v.Major() < c.con.Major() { - return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) + return false, fmt.Errorf("%q is less than or equal to %q", v, c.orig) } else if c.minorDirty { // This is a range case such as >11. When the version is something like // 11.1.0 is it not > 11. For that we would need 12 or higher - return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) + return false, fmt.Errorf("%q is less than or equal to %q", v, c.orig) } else if c.patchDirty { // This is for ranges such as >11.1. A version of 11.1.1 is not greater // which one of 11.2.1 is greater @@ -392,7 +414,7 @@ func constraintGreaterThan(v *Version, c *constraint, includePre bool) (bool, er if eq { return true, nil } - return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) + return false, fmt.Errorf("%q is less than or equal to %q", v, c.orig) } // If we have gotten here we are not comparing pre-preleases and can use the @@ -401,21 +423,21 @@ func constraintGreaterThan(v *Version, c *constraint, includePre bool) (bool, er if eq { return true, nil } - return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig) + return false, fmt.Errorf("%q is less than or equal to %q", v, c.orig) } func constraintLessThan(v *Version, c *constraint, includePre bool) (bool, error) { // The existence of prereleases is checked at the group level and passed in. // Exit early if the version has a prerelease but those are to be ignored. if v.Prerelease() != "" && !includePre { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + return false, fmt.Errorf("%q is a prerelease version and the constraint is only looking for release versions", v) } eq := v.Compare(c.con) < 0 if eq { return true, nil } - return false, fmt.Errorf("%s is greater than or equal to %s", v, c.orig) + return false, fmt.Errorf("%q is greater than or equal to %q", v, c.orig) } func constraintGreaterThanEqual(v *Version, c *constraint, includePre bool) (bool, error) { @@ -423,21 +445,21 @@ func constraintGreaterThanEqual(v *Version, c *constraint, includePre bool) (boo // The existence of prereleases is checked at the group level and passed in. // Exit early if the version has a prerelease but those are to be ignored. if v.Prerelease() != "" && !includePre { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + return false, fmt.Errorf("%q is a prerelease version and the constraint is only looking for release versions", v) } eq := v.Compare(c.con) >= 0 if eq { return true, nil } - return false, fmt.Errorf("%s is less than %s", v, c.orig) + return false, fmt.Errorf("%q is less than %q", v, c.orig) } func constraintLessThanEqual(v *Version, c *constraint, includePre bool) (bool, error) { // The existence of prereleases is checked at the group level and passed in. // Exit early if the version has a prerelease but those are to be ignored. if v.Prerelease() != "" && !includePre { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + return false, fmt.Errorf("%q is a prerelease version and the constraint is only looking for release versions", v) } var eq bool @@ -447,13 +469,13 @@ func constraintLessThanEqual(v *Version, c *constraint, includePre bool) (bool, if eq { return true, nil } - return false, fmt.Errorf("%s is greater than %s", v, c.orig) + return false, fmt.Errorf("%q is greater than %q", v, c.orig) } if v.Major() > c.con.Major() { - return false, fmt.Errorf("%s is greater than %s", v, c.orig) + return false, fmt.Errorf("%q is greater than %q", v, c.orig) } else if v.Major() == c.con.Major() && v.Minor() > c.con.Minor() && !c.minorDirty { - return false, fmt.Errorf("%s is greater than %s", v, c.orig) + return false, fmt.Errorf("%q is greater than %q", v, c.orig) } return true, nil @@ -469,11 +491,11 @@ func constraintTilde(v *Version, c *constraint, includePre bool) (bool, error) { // The existence of prereleases is checked at the group level and passed in. // Exit early if the version has a prerelease but those are to be ignored. if v.Prerelease() != "" && !includePre { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + return false, fmt.Errorf("%q is a prerelease version and the constraint is only looking for release versions", v) } if v.LessThan(c.con) { - return false, fmt.Errorf("%s is less than %s", v, c.orig) + return false, fmt.Errorf("%q is less than %q", v, c.orig) } // ~0.0.0 is a special case where all constraints are accepted. It's @@ -484,11 +506,11 @@ func constraintTilde(v *Version, c *constraint, includePre bool) (bool, error) { } if v.Major() != c.con.Major() { - return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig) + return false, fmt.Errorf("%q does not have same major version as %q", v, c.orig) } if v.Minor() != c.con.Minor() && !c.minorDirty { - return false, fmt.Errorf("%s does not have same major and minor version as %s", v, c.orig) + return false, fmt.Errorf("%q does not have same major and minor version as %q", v, c.orig) } return true, nil @@ -500,7 +522,7 @@ func constraintTildeOrEqual(v *Version, c *constraint, includePre bool) (bool, e // The existence of prereleases is checked at the group level and passed in. // Exit early if the version has a prerelease but those are to be ignored. if v.Prerelease() != "" && !includePre { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + return false, fmt.Errorf("%q is a prerelease version and the constraint is only looking for release versions", v) } if c.dirty { @@ -512,7 +534,7 @@ func constraintTildeOrEqual(v *Version, c *constraint, includePre bool) (bool, e return true, nil } - return false, fmt.Errorf("%s is not equal to %s", v, c.orig) + return false, fmt.Errorf("%q is not equal to %q", v, c.orig) } // ^* --> (any) @@ -528,12 +550,12 @@ func constraintCaret(v *Version, c *constraint, includePre bool) (bool, error) { // The existence of prereleases is checked at the group level and passed in. // Exit early if the version has a prerelease but those are to be ignored. if v.Prerelease() != "" && !includePre { - return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v) + return false, fmt.Errorf("%q is a prerelease version and the constraint is only looking for release versions", v) } // This less than handles prereleases if v.LessThan(c.con) { - return false, fmt.Errorf("%s is less than %s", v, c.orig) + return false, fmt.Errorf("%q is less than %q", v, c.orig) } var eq bool @@ -548,12 +570,12 @@ func constraintCaret(v *Version, c *constraint, includePre bool) (bool, error) { if eq { return true, nil } - return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig) + return false, fmt.Errorf("%q does not have same major version as %q", v, c.orig) } // ^ when the major is 0 and minor > 0 is >=0.y.z < 0.y+1 if c.con.Major() == 0 && v.Major() > 0 { - return false, fmt.Errorf("%s does not have same major version as %s", v, c.orig) + return false, fmt.Errorf("%q does not have same major version as %q", v, c.orig) } // If the con Minor is > 0 it is not dirty if c.con.Minor() > 0 || c.patchDirty { @@ -561,11 +583,11 @@ func constraintCaret(v *Version, c *constraint, includePre bool) (bool, error) { if eq { return true, nil } - return false, fmt.Errorf("%s does not have same minor version as %s. Expected minor versions to match when constraint major version is 0", v, c.orig) + return false, fmt.Errorf("%q does not have same minor version as %q. Expected minor versions to match when constraint major version is 0", v, c.orig) } // ^ when the minor is 0 and minor > 0 is =0.0.z if c.con.Minor() == 0 && v.Minor() > 0 { - return false, fmt.Errorf("%s does not have same minor version as %s", v, c.orig) + return false, fmt.Errorf("%q does not have same minor version as %q", v, c.orig) } // At this point the major is 0 and the minor is 0 and not dirty. The patch @@ -574,7 +596,7 @@ func constraintCaret(v *Version, c *constraint, includePre bool) (bool, error) { if eq { return true, nil } - return false, fmt.Errorf("%s does not equal %s. Expect version and constraint to equal when major and minor versions are 0", v, c.orig) + return false, fmt.Errorf("%q does not equal %q. Expect version and constraint to equal when major and minor versions are 0", v, c.orig) } func isX(x string) bool { diff --git a/vendor/github.com/Masterminds/semver/v3/version.go b/vendor/github.com/Masterminds/semver/v3/version.go index 7a3ba738..da428760 100644 --- a/vendor/github.com/Masterminds/semver/v3/version.go +++ b/vendor/github.com/Masterminds/semver/v3/version.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "math" "regexp" "strconv" "strings" @@ -48,8 +49,16 @@ var ( // ErrInvalidPrerelease is returned when the pre-release is an invalid format ErrInvalidPrerelease = errors.New("invalid prerelease string") + + // ErrVersionTooLong is returned when a version string exceeds the + // maximum allowed length. + ErrVersionTooLong = fmt.Errorf("version string is too long (max %d bytes)", MaxVersionLen) ) +// MaxVersionLen is the maximum allowed length of a version string. This guards +// against unbounded input causing excessive memory allocations during parsing. +const MaxVersionLen = 256 + // semVerRegex is the regular expression used to parse a semantic version. // This is not the official regex from the semver spec. It has been modified to allow for loose handling // where versions like 2.1 are detected. @@ -94,6 +103,10 @@ func StrictNewVersion(v string) (*Version, error) { return nil, ErrEmptyString } + if len(v) > MaxVersionLen { + return nil, ErrVersionTooLong + } + // Split the parts into [0]major, [1]minor, and [2]patch,prerelease,build parts := strings.SplitN(v, ".", 3) if len(parts) != 3 { @@ -161,6 +174,9 @@ func StrictNewVersion(v string) (*Version, error) { // attempts to convert it to SemVer. If you want to validate it was a strict // semantic version at parse time see StrictNewVersion(). func NewVersion(v string) (*Version, error) { + if len(v) > MaxVersionLen { + return nil, ErrVersionTooLong + } if CoerceNewVersion { return coerceNewVersion(v) } @@ -289,6 +305,8 @@ func coerceNewVersion(v string) (*Version, error) { // New creates a new instance of Version with each of the parts passed in as // arguments instead of parsing a version string. +// Note, New does not validate prerelease or metadata. Incorrect information can +// be passed in. func New(major, minor, patch uint64, pre, metadata string) *Version { v := Version{ major: major, @@ -301,6 +319,7 @@ func New(major, minor, patch uint64, pre, metadata string) *Version { v.original = v.String() + // TODO: In the next semver major version validate the pre and metadata. Return error if there is one. return &v } @@ -388,6 +407,9 @@ func (v Version) IncPatch() Version { } else { vNext.metadata = "" vNext.pre = "" + if v.patch == math.MaxUint64 { + panic("patch version increment would overflow uint64") + } vNext.patch = v.patch + 1 } vNext.original = v.originalVPrefix() + "" + vNext.String() @@ -404,6 +426,9 @@ func (v Version) IncMinor() Version { vNext.metadata = "" vNext.pre = "" vNext.patch = 0 + if v.minor == math.MaxUint64 { + panic("minor version increment would overflow uint64") + } vNext.minor = v.minor + 1 vNext.original = v.originalVPrefix() + "" + vNext.String() return vNext @@ -421,6 +446,9 @@ func (v Version) IncMajor() Version { vNext.pre = "" vNext.patch = 0 vNext.minor = 0 + if v.major == math.MaxUint64 { + panic("major version increment would overflow uint64") + } vNext.major = v.major + 1 vNext.original = v.originalVPrefix() + "" + vNext.String() return vNext @@ -568,7 +596,16 @@ func (v Version) MarshalText() ([]byte, error) { // Scan implements the SQL.Scanner interface. func (v *Version) Scan(value interface{}) error { var s string - s, _ = value.(string) + switch t := value.(type) { + case string: + s = t + case []byte: + s = string(t) + case nil: + return fmt.Errorf("cannot scan nil into Version") + default: + return fmt.Errorf("unsupported Scan type %T", value) + } temp, err := NewVersion(s) if err != nil { return err diff --git a/vendor/github.com/alecthomas/chroma/v2/.golangci.yml b/vendor/github.com/alecthomas/chroma/v2/.golangci.yml index 91f313b1..a345ac65 100644 --- a/vendor/github.com/alecthomas/chroma/v2/.golangci.yml +++ b/vendor/github.com/alecthomas/chroma/v2/.golangci.yml @@ -1,89 +1,122 @@ +version: "2" run: tests: true - output: - print-issued-lines: false - + show-stats: false + formats: + text: + print-issued-lines: false + colors: true linters: - enable-all: true + default: all disable: - - lll - - gocyclo + - exhaustive + - prealloc - dupl - - gochecknoglobals - - funlen - - godox - - wsl - - gocognit - - nolintlint - - testpackage - - godot - - nestif - - paralleltest - - nlreturn + - godoclint - cyclop - - gci - - gofumpt - - errorlint - - exhaustive - - wrapcheck - - stylecheck - - thelper - - nonamedreturns - - revive + - depguard - dupword + - err113 + - errname + - errorlint - exhaustruct - - varnamelen + - forbidigo - forcetypeassert + - funlen + - gochecknoglobals + - gocognit + - gocritic + - gocyclo + - godot + - godox + - gomoddirectives - ireturn + - lll - maintidx - - govet - - testableexamples - - musttag - - depguard - - goconst - - perfsprint - mnd + - nakedret + - nestif + - nilnil + - nlreturn + - nolintlint + - nonamedreturns + - paralleltest + - perfsprint - predeclared - recvcheck - - tenv - - err113 - -linters-settings: - gocyclo: - min-complexity: 10 - dupl: - threshold: 100 - goconst: - min-len: 8 - min-occurrences: 3 - forbidigo: - #forbid: - # - (Must)?NewLexer$ - exclude_godoc_examples: false - - + - revive + - testpackage + - varnamelen + - wastedassign + - whitespace + - wsl + - wsl_v5 + - funcorder + - noinlineerr + - tagalign + - goconst + - gochecknoinits + - durationcheck + - embeddedstructfieldcheck + - wrapcheck + - gomodguard + settings: + dupl: + threshold: 100 + exhaustive: + default-signifies-exhaustive: true + goconst: + min-len: 8 + min-occurrences: 3 + gocyclo: + min-complexity: 10 + wrapcheck: + report-internal-errors: false + ignore-package-globs: + - github.com/alecthomas/errors + exclusions: + generated: lax + rules: + - path: (.+)\.go$ + text: "^(G104|G204|G307|G304):" + - path: (.+)\.go$ + text: Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked + - path: (.+)\.go$ + text: exported method `(.*\.MarshalJSON|.*\.UnmarshalJSON|.*\.EntityURN|.*\.GoString|.*\.Pos)` should have comment or be unexported + - path: (.+)\.go$ + text: uses unkeyed fields + - path: (.+)\.go$ + text: declaration of "err" shadows declaration + - path: (.+)\.go$ + text: bad syntax for struct tag key + - path: (.+)\.go$ + text: bad syntax for struct tag pair + - path: (.+)\.go$ + text: ^ST1012 + - path: (.+)\.go$ + text: log/slog.Logger.*must not be called + - path: (.+)_test\.go$ + text: error returned from external package is unwrapped + - linters: [staticcheck] + text: QF1008 + - text: "Error return value of `.*.Write` is not checked" + linters: [errcheck] + path: (.+)_test\.go$ + paths: + - third_party$ + - builtin$ + - examples$ issues: - exclude-dirs: - - _examples - max-per-linter: 0 - max-same: 0 - exclude-use-default: false - exclude: - # Captured by errcheck. - - '^(G104|G204):' - # Very commonly not checked. - - 'Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked' - - 'exported method (.*\.MarshalJSON|.*\.UnmarshalJSON|.*\.EntityURN|.*\.GoString|.*\.Pos) should have comment or be unexported' - - 'composite literal uses unkeyed fields' - - 'declaration of "err" shadows declaration' - - 'should not use dot imports' - - 'Potential file inclusion via variable' - - 'should have comment or be unexported' - - 'comment on exported var .* should be of the form' - - 'at least one file in a package should have a package comment' - - 'string literal contains the Unicode' - - 'methods on the same type should have the same receiver name' - - '_TokenType_name should be _TokenTypeName' - - '`_TokenType_map` should be `_TokenTypeMap`' - - 'rewrite if-else to switch statement' + max-issues-per-linter: 0 + max-same-issues: 0 +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/vendor/github.com/alecthomas/chroma/v2/AGENTS.md b/vendor/github.com/alecthomas/chroma/v2/AGENTS.md index 0d3b6ee4..d15d770a 100644 --- a/vendor/github.com/alecthomas/chroma/v2/AGENTS.md +++ b/vendor/github.com/alecthomas/chroma/v2/AGENTS.md @@ -1,6 +1,6 @@ Chroma is a syntax highlighting library, tool and web playground for Go. It is based on Pygments and includes importers for it, so most of the same concepts from Pygments apply to Chroma. -This project is written in Go, uses Hermit to manage tooling, and Just for helper commands. Helper scripts are in ./scripts. +This project is written in Go, uses Hermit to manage tooling, and Just for helper commands. Helper tooling is primarily in ./_tools. Language definitions are XML files defined in ./lexers/embedded/*.xml. diff --git a/vendor/github.com/alecthomas/chroma/v2/BUILD.bit b/vendor/github.com/alecthomas/chroma/v2/BUILD.bit new file mode 100644 index 00000000..f8ee1aee --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/BUILD.bit @@ -0,0 +1,93 @@ +let version = exec("git describe --tags --dirty --always") | trim +# TinyGo's installation root; used to source `wasm_exec.js`. +let tinygoroot = exec("tinygo env TINYGOROOT") | trim + + +# Generate tokentype_enumer.go from types.go via `//go:generate`. +tokentype = go.generate { + package = "." + inputs = ["types.go"] + outputs = ["tokentype_enumer.go"] +} + +# Regenerate the lexer table in README.md by invoking the host `chroma` binary. +# GOOS/GOARCH are cleared so cross-compile env vars don't break the local run. +protected readme = exec { + command = "./table.py" + inputs = ["table.py", "lexers/**/*.go", "lexers/**/*.xml"] + output = "README.md" +} + +# Format frontend JS sources in place. Runs as a sub-step of `index-min-js`, +# so bundling always sees formatted sources. +format-js = exec { + command = "biome format --write cmd/chromad/static/index.js cmd/chromad/static/chroma.js" + inputs = ["biome.js", "cmd/chromad/static/index.js", "cmd/chromad/static/chroma.js"] +} + +# Copy TinyGo's wasm_exec.js into the chromad static assets. +wasm-exec = exec { + command = "install -m644 '#{tinygoroot}/targets/wasm_exec.js' cmd/chromad/static/wasm_exec.js" + resolve = "sha256 '#{tinygoroot}/targets/wasm_exec.js'" + output = "cmd/chromad/static/wasm_exec.js" +} + +# Build the chroma WASM module via tinygo (installed via hermit) for the +# smaller output binary. +chroma-wasm = exec { + command = "tinygo build -no-debug -target wasm -o cmd/chromad/static/chroma.wasm cmd/libchromawasm/main.go" + inputs = ["cmd/libchromawasm/**/*.go", "*.go", "lexers/**/*.go", "lexers/**/*.xml", "formatters/**/*.go", "styles/**/*.go"] + output = "cmd/chromad/static/chroma.wasm" +} + +# Bundle and minify the frontend JS. Depends on `format-js` so the bundle +# always reflects formatted sources. +index-min-js = exec { + command = "esbuild --platform=browser --format=esm --bundle cmd/chromad/static/index.js --minify --external:./wasm_exec.js --outfile=cmd/chromad/static/index.min.js" + inputs = ["cmd/chromad/static/index.js", "cmd/chromad/static/chroma.js"] + output = "cmd/chromad/static/index.min.js" + depends_on = [format-js] +} + +# Bundle and minify the frontend CSS. +index-min-css = exec { + command = "esbuild --bundle cmd/chromad/static/index.css --minify --outfile=cmd/chromad/static/index.min.css" + inputs = ["cmd/chromad/static/index.css", "cmd/chromad/static/bulma.css"] + output = "cmd/chromad/static/index.min.css" +} + +# Build the chromad server binary. cmd/chromad is a separate Go module, so +# `dir` puts the build in there and `package = "."` resolves against that +# module. `output` stays project-root-relative; bit absolutises it before +# passing to `go build -o`. Defaults to linux/amd64 to match the deploy +# target; override with GOOS/GOARCH env vars for local builds. +chromad = go.exe { + dir = "cmd/chromad" + package = "." + output = "build/chromad" + flags = ["-ldflags", "-X 'main.version=#{version}'"] + goos = env("GOOS", "linux") + goarch = env("GOARCH", "amd64") + cgo = false + depends_on = [wasm-exec, chroma-wasm, index-min-js, index-min-css, test] +} + +pre format-go = go.fmt { + package = "./..." +} + +# Run Go tests. +test = go.test { + package = "./..." +} + +# Deploy chromad to swapoff.org. Must be explicitly selected. +explicit upload = exec { + command = <<-EOF + scp #{chromad.path} root@swapoff.org: + ssh root@swapoff.org 'install -m755 ./chromad /srv/http/swapoff.org/bin && service chromad restart' + EOF + depends_on = [chromad] +} + +target default = [test, chromad, readme, tokentype] diff --git a/vendor/github.com/alecthomas/chroma/v2/Bitfile b/vendor/github.com/alecthomas/chroma/v2/Bitfile deleted file mode 100644 index bf158633..00000000 --- a/vendor/github.com/alecthomas/chroma/v2/Bitfile +++ /dev/null @@ -1,24 +0,0 @@ -VERSION = %(git describe --tags --dirty --always)% -export CGOENABLED = 0 - -tokentype_enumer.go: types.go - build: go generate - -# Regenerate the list of lexers in the README -README.md: lexers/*.go lexers/*/*.xml table.py - build: ./table.py - -clean - -implicit %{1}%{2}.min.%{3}: **/*.{css,js} - build: esbuild --bundle %{IN} --minify --outfile=%{OUT} - -implicit build/%{1}: cmd/* - cd cmd/%{1} - inputs: cmd/%{1}/**/* **/*.go - build: go build -ldflags="-X 'main.version=%{VERSION}'" -o ../../build/%{1} . - -#upload: chromad -# build: -# scp chromad root@swapoff.org: -# ssh root@swapoff.org 'install -m755 ./chromad /srv/http/swapoff.org/bin && service chromad restart' -# touch upload diff --git a/vendor/github.com/alecthomas/chroma/v2/COPYING b/vendor/github.com/alecthomas/chroma/v2/COPYING index 92dc39f7..33da4898 100644 --- a/vendor/github.com/alecthomas/chroma/v2/COPYING +++ b/vendor/github.com/alecthomas/chroma/v2/COPYING @@ -17,3 +17,102 @@ 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. + + +// formatters/svg/font_liberation_mono.go + +Digitized data copyright (c) 2010 Google Corporation +with Reserved Font Arimo, Tinos and Cousine. +Copyright (c) 2012 Red Hat, Inc. +with Reserved Font Name Liberation. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/vendor/github.com/alecthomas/chroma/v2/Dockerfile b/vendor/github.com/alecthomas/chroma/v2/Dockerfile index c86b3793..dc10ec75 100644 --- a/vendor/github.com/alecthomas/chroma/v2/Dockerfile +++ b/vendor/github.com/alecthomas/chroma/v2/Dockerfile @@ -1,7 +1,7 @@ # Multi-stage Dockerfile for chromad Go application using Hermit-managed tools # Build stage -FROM ubuntu:24.04 AS builder +FROM ubuntu:26.04 AS builder # Install system dependencies RUN apt-get update && apt-get install -y \ @@ -28,7 +28,7 @@ ENV GOARCH=amd64 RUN just chromad # Runtime stage -FROM alpine:3.23 AS runtime +FROM alpine:3.24 AS runtime # Install ca-certificates for HTTPS requests RUN apk --no-cache add ca-certificates curl diff --git a/vendor/github.com/alecthomas/chroma/v2/Justfile b/vendor/github.com/alecthomas/chroma/v2/Justfile index 24e3816f..4db3dc31 100644 --- a/vendor/github.com/alecthomas/chroma/v2/Justfile +++ b/vendor/github.com/alecthomas/chroma/v2/Justfile @@ -1,4 +1,4 @@ -set positional-arguments := true +set positional-arguments set shell := ["bash", "-c"] version := `git describe --tags --dirty --always` @@ -21,6 +21,10 @@ tokentype-string: format-js: biome format --write cmd/chromad/static/index.js cmd/chromad/static/chroma.js +# Tidy Go modules +tidy: + find . -name 'go.mod' -execdir go mod tidy \; + # Build chromad binary chromad: wasm-exec chroma-wasm #!/usr/bin/env bash diff --git a/vendor/github.com/alecthomas/chroma/v2/README.md b/vendor/github.com/alecthomas/chroma/v2/README.md index 4aeb54d9..df962da7 100644 --- a/vendor/github.com/alecthomas/chroma/v2/README.md +++ b/vendor/github.com/alecthomas/chroma/v2/README.md @@ -36,25 +36,25 @@ translators for Pygments lexers and styles. | Prefix | Language | :----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -| A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Agda, AL, Alloy, Angular2, ANTLR, ApacheConf, APL, AppleScript, ArangoDB AQL, Arduino, ArmAsm, ATL, AutoHotkey, AutoIt, Awk +| A | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Agda, AL, Alloy, AMPL, Angular2, ANTLR, ApacheConf, APL, AppleScript, ArangoDB AQL, Arduino, ArmAsm, Arturo, ATL, AutoHotkey, AutoIt, Awk | B | Ballerina, Bash, Bash Session, Batchfile, Beef, BibTeX, Bicep, BlitzBasic, BNF, BQN, Brainfuck | C | C, C#, C++, C3, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Chapel, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Core, Crystal, CSS, CSV, CUE, Cython -| D | D, Dart, Dax, Desktop file, Diff, Django/Jinja, dns, Docker, DTD, Dylan -| E | EBNF, Elixir, Elm, EmacsLisp, Erlang +| D | D, Dart, Dax, Desktop file, Devicetree, Diff, Django/Jinja, dns, Docker, DTD, Dylan +| E | EBNF, Elixir, Elm, EmacsLisp, ERB, Erlang | F | Factor, Fennel, Fish, Forth, Fortran, FortranFixed, FSharp -| G | GAS, GDScript, GDScript3, Gemtext, Genshi, Genshi HTML, Genshi Text, Gherkin, Gleam, GLSL, Gnuplot, Go, Go HTML Template, Go Template, Go Text Template, GraphQL, Groff, Groovy +| G | GAS, GDScript, GDScript3, Gemtext, Genshi, Genshi HTML, Genshi Text, Gettext, Gherkin, Gleam, GLSL, Gnuplot, Go, Go HTML Template, Go Template, Go Text Template, GraphQL, Groff, Groovy | H | Handlebars, Hare, Haskell, Haxe, HCL, Hexdump, HLB, HLSL, HolyC, HTML, HTTP, Hy | I | Idris, Igor, INI, Io, ISCdhcpd | J | J, Janet, Java, JavaScript, JSON, JSONata, Jsonnet, Julia, Jungle -| K | Kakoune, Kotlin -| L | Lean4, Lighttpd configuration file, LLVM, lox, Lua -| M | Makefile, Mako, markdown, Markless, Mason, Materialize SQL dialect, Mathematica, Matlab, MCFunction, Meson, Metal, MiniZinc, MLIR, Modelica, Modula-2, Mojo, MonkeyC, MoonScript, MorrowindScript, Myghty, MySQL +| K | Kakoune, KDL, Kotlin +| L | Lateralus, Lean4, Lighttpd configuration file, LilyPond, LLVM, lox, Lua, Luau +| M | Makefile, Mako, markdown, Markless, Mason, Materialize SQL dialect, Mathematica, Matlab, MCFunction, Meson, Metal, microcad, MiniZinc, MLIR, Modelica, Modula-2, Mojo, MonkeyC, MoonBit, MoonScript, MorrowindScript, Myghty, MySQL | N | NASM, Natural, NDISASM, Newspeak, Nginx configuration file, Nim, Nix, NSIS, Nu | O | Objective-C, ObjectPascal, OCaml, Octave, Odin, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode | P | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Plutus Core, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerQuery, PowerShell, Prolog, Promela, PromQL, properties, Protocol Buffer, Protocol Buffer Text Format, PRQL, PSL, Puppet, Python, Python 2 | Q | QBasic, QML | R | R, Racket, Ragel, Raku, react, ReasonML, reg, Rego, reStructuredText, Rexx, RGBDS Assembly, Ring, RPGLE, RPMSpec, Ruby, Rust -| S | SAS, Sass, Scala, Scheme, Scilab, SCSS, Sed, Sieve, Smali, Smalltalk, Smarty, SNBT, Snobol, Solidity, SourcePawn, SPARQL, SQL, SquidConf, Standard ML, stas, Stylus, Svelte, Swift, SYSTEMD, systemverilog +| S | SAS, Sass, Scala, scdoc, Scheme, Scilab, SCSS, Sed, Sieve, Smali, Smalltalk, Smarty, SNBT, Snobol, Solidity, SourcePawn, Spade, SPARQL, SQL, SquidConf, Standard ML, stas, Stylus, Svelte, Swift, SYSTEMD, systemverilog | T | TableGen, Tal, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData, Typst | U | ucode | V | V, V shell, Vala, VB.net, verilog, VHDL, VHS, VimL, vue @@ -226,11 +226,11 @@ formatter outputs raw tokens. The latter is useful for debugging lexers. ### Styles Chroma styles are defined in XML. The style entries use the -[same syntax](http://pygments.org/docs/styles/) as Pygments. - -All Pygments styles have been converted to Chroma using the `_tools/style.py` +[same syntax](http://pygments.org/docs/styles/) as Pygments. All Pygments styles have been converted to Chroma using the `_tools/style.py` script. +Style names are case-insensitive. For example, `monokai` and `Monokai` are treated as the same style. + When you work with one of [Chroma's styles](https://github.com/alecthomas/chroma/tree/master/styles), know that the `Background` token type provides the default style for tokens. It does so by defining a foreground color and background color. @@ -276,6 +276,8 @@ for that setup the `chroma` executable can be just symlinked to `~/.lessfilter`. its input using Chroma * [Hugo](https://gohugo.io/) is a static site generator that [uses Chroma for syntax highlighting code examples](https://gohugo.io/content-management/syntax-highlighting/) +* [f4](https://github.com/unxed/f4) is asynchronious cross platform Far Manager clone in Go + that uses Chroma for syntax highlighting in built-in editor ## Testing lexers diff --git a/vendor/github.com/alecthomas/chroma/v2/colour.go b/vendor/github.com/alecthomas/chroma/v2/colour.go index e33d0106..8add068d 100644 --- a/vendor/github.com/alecthomas/chroma/v2/colour.go +++ b/vendor/github.com/alecthomas/chroma/v2/colour.go @@ -52,7 +52,7 @@ type Colour int32 // NewColour creates a Colour directly from RGB values. func NewColour(r, g, b uint8) Colour { - return ParseColour(fmt.Sprintf("%02x%02x%02x", r, g, b)) + return Colour(int32(r)<<16|int32(g)<<8|int32(b)) + 1 } // Distance between this colour and another. diff --git a/vendor/github.com/alecthomas/chroma/v2/formatters/html/html.go b/vendor/github.com/alecthomas/chroma/v2/formatters/html/html.go index 1aaafd0f..e876aab7 100644 --- a/vendor/github.com/alecthomas/chroma/v2/formatters/html/html.go +++ b/vendor/github.com/alecthomas/chroma/v2/formatters/html/html.go @@ -4,6 +4,7 @@ import ( "fmt" "html" "io" + "slices" "sort" "strconv" "strings" @@ -83,6 +84,11 @@ func WithPreWrapper(wrapper PreWrapper) Option { } } +// WithModeClasses adds the style's mode (eg. "light" or "dark") as a CSS +// class on wrapper elements and scopes WriteCSS rules by mode. This enables +// combining light and dark stylesheets and switching themes at runtime. +func WithModeClasses(b bool) Option { return func(f *Formatter) { f.modeClasses = b } } + // WrapLongLines wraps long lines. func WrapLongLines(b bool) Option { return func(f *Formatter) { @@ -206,6 +212,7 @@ type Formatter struct { inlineCode bool preventSurroundingPre bool tabWidth int + modeClasses bool wrapLongLines bool lineNumbers bool lineNumbersInTable bool @@ -241,7 +248,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma. fmt.Fprintf(w, "body { %s; }\n", css[chroma.Background]) fmt.Fprint(w, "") } - fmt.Fprintf(w, "\n", f.styleAttr(css, chroma.Background)) + fmt.Fprintf(w, "\n", f.styleAttrWithMode(css, chroma.Background, style)) } wrapInTable := f.lineNumbers && f.lineNumbersInTable @@ -252,10 +259,10 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma. if wrapInTable { // List line numbers in its own - fmt.Fprintf(w, "\n", f.styleAttr(css, chroma.PreWrapper)) + fmt.Fprintf(w, "\n", f.styleAttrWithMode(css, chroma.PreWrapper, style)) fmt.Fprintf(w, "", f.styleAttr(css, chroma.LineTable)) fmt.Fprintf(w, "\n", f.styleAttr(css, chroma.LineTableTD)) - fmt.Fprintf(w, "%s", f.preWrapper.Start(false, f.styleAttr(css, chroma.PreWrapper))) + fmt.Fprintf(w, "%s", f.preWrapper.Start(false, f.styleAttrWithMode(css, chroma.PreWrapper, style))) for index := range lines { line := f.baseLineNumber + index highlight, next := f.shouldHighlight(highlightIndex, line) @@ -277,7 +284,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma. fmt.Fprintf(w, "\n", f.styleAttr(css, chroma.LineTableTD, "width:100%")) } - fmt.Fprintf(w, "%s", f.preWrapper.Start(true, f.styleAttr(css, chroma.PreWrapper))) + fmt.Fprintf(w, "%s", f.preWrapper.Start(true, f.styleAttrWithMode(css, chroma.PreWrapper, style))) highlightIndex = 0 for index, tokens := range lines { @@ -288,7 +295,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma. highlightIndex++ } - if !(f.preventSurroundingPre || f.inlineCode) { + if !f.preventSurroundingPre && !f.inlineCode { // Start of Line fmt.Fprint(w, ``) // End of CodeLine fmt.Fprint(w, ``) // End of Line @@ -414,6 +421,26 @@ func (f *Formatter) styleAttr(styles map[chroma.TokenType]string, tt chroma.Toke return fmt.Sprintf(` style="%s"`, strings.Join(css, ";")) } +// modeClass returns the CSS class corresponding to the style's mode (eg. +// "light" or "dark"), with the formatter's class prefix applied. +func (f *Formatter) modeClass(style *chroma.Style) string { + return f.prefix + style.Mode().String() +} + +// styleAttrWithMode is like styleAttr but, in classes mode, appends the +// style's mode class alongside the existing class. Used for the outer +// wrapper and standalone so external CSS can target the mode. +func (f *Formatter) styleAttrWithMode(styles map[chroma.TokenType]string, tt chroma.TokenType, style *chroma.Style) string { + if !f.Classes || !f.modeClasses { + return f.styleAttr(styles, tt) + } + cls := f.class(tt) + if cls == "" { + return "" + } + return fmt.Sprintf(` class="%s %s"`, cls, f.modeClass(style)) +} + func (f *Formatter) tabWidthStyle() string { if f.tabWidth != 0 && f.tabWidth != 8 { return fmt.Sprintf("-moz-tab-size: %[1]d; -o-tab-size: %[1]d; tab-size: %[1]d;", f.tabWidth) @@ -437,20 +464,38 @@ func (f *Formatter) writeCSSRule(w io.Writer, comment string, selector string, s } // WriteCSS writes CSS style definitions (without any surrounding HTML). +// +// Rules are scoped by the style's mode (eg. ".chroma.dark") so that CSS +// generated from a light and dark style can be combined without conflict. +// To support dynamic theme switching, call WriteCSS with both styles, +// concatenate the output, and toggle the wrapper's mode class (added +// automatically by Format) at runtime. Tokens that one theme leaves +// unstyled fall back to that theme's ".chroma." text/background +// via the CSS cascade; pass WithAllClasses(true) if you need every +// token's rule materialised explicitly for both themes. func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error { css := f.styleCache.get(style, false) + var chromaSel, bgSel string + if f.modeClasses { + modeCls := f.modeClass(style) + chromaSel = fmt.Sprintf(".%schroma.%s", f.prefix, modeCls) + bgSel = fmt.Sprintf(".%sbg.%s", f.prefix, modeCls) + } else { + chromaSel = fmt.Sprintf(".%schroma", f.prefix) + bgSel = fmt.Sprintf(".%sbg", f.prefix) + } // Special-case background as it is mapped to the outer ".chroma" class. - if err := f.writeCSSRule(w, chroma.Background.String(), fmt.Sprintf(".%sbg", f.prefix), css[chroma.Background]); err != nil { + if err := f.writeCSSRule(w, chroma.Background.String(), bgSel, css[chroma.Background]); err != nil { return err } // Special-case PreWrapper as it is the ".chroma" class. - if err := f.writeCSSRule(w, chroma.PreWrapper.String(), fmt.Sprintf(".%schroma", f.prefix), css[chroma.PreWrapper]); err != nil { + if err := f.writeCSSRule(w, chroma.PreWrapper.String(), chromaSel, css[chroma.PreWrapper]); err != nil { return err } // Special-case code column of table to expand width. if f.lineNumbers && f.lineNumbersInTable { - selector := fmt.Sprintf(".%schroma .%s:last-child", f.prefix, f.class(chroma.LineTableTD)) + selector := fmt.Sprintf("%s .%s:last-child", chromaSel, f.class(chroma.LineTableTD)) if err := f.writeCSSRule(w, chroma.LineTableTD.String(), selector, "width: 100%;"); err != nil { return err } @@ -460,7 +505,7 @@ func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error { targetedLineCSS := StyleEntryToCSS(style.Get(chroma.LineHighlight)) for _, tt := range []chroma.TokenType{chroma.LineNumbers, chroma.LineNumbersTable} { comment := fmt.Sprintf("%s targeted by URL anchor", tt) - selector := fmt.Sprintf(".%schroma .%s:target", f.prefix, f.class(tt)) + selector := fmt.Sprintf("%s .%s:target", chromaSel, f.class(tt)) if err := f.writeCSSRule(w, comment, selector, targetedLineCSS); err != nil { return err } @@ -481,7 +526,7 @@ func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error { if class == "" { continue } - if err := f.writeCSSRule(w, tt.String(), fmt.Sprintf(".%schroma .%s", f.prefix, class), css[tt]); err != nil { + if err := f.writeCSSRule(w, tt.String(), fmt.Sprintf("%s .%s", chromaSel, class), css[tt]); err != nil { return err } } @@ -613,8 +658,7 @@ func (l *styleCache) get(style *chroma.Style, compress bool) map[chroma.TokenTyp defer l.mu.Unlock() // Look for an existing entry. - for i := len(l.cache) - 1; i >= 0; i-- { - entry := l.cache[i] + for i, entry := range slices.Backward(l.cache) { if entry.style == style && entry.compressed == compress { // Top of the cache, no need to adjust the order. if i == len(l.cache)-1 { diff --git a/vendor/github.com/alecthomas/chroma/v2/formatters/svg/font_liberation_mono.go b/vendor/github.com/alecthomas/chroma/v2/formatters/svg/font_liberation_mono.go index 70d692ec..416208a7 100644 --- a/vendor/github.com/alecthomas/chroma/v2/formatters/svg/font_liberation_mono.go +++ b/vendor/github.com/alecthomas/chroma/v2/formatters/svg/font_liberation_mono.go @@ -4,7 +4,7 @@ // with Reserved Font Name Liberation. // // This Font Software is licensed under the SIL Open Font License, Version 1.1. -// This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL +// This license is copied below, and is also available with a FAQ at: https://openfontlicense.org // // ----------------------------------------------------------- // SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ampl.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ampl.xml new file mode 100644 index 00000000..8c2479e5 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ampl.xml @@ -0,0 +1,98 @@ + + + AMPL + ampl + *.mod + *.run + text/x-ampl + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arturo.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arturo.xml new file mode 100644 index 00000000..541dc46e --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/arturo.xml @@ -0,0 +1,119 @@ + + + + Arturo + arturo + art + *.art + + + + + + + + + + + + + + + + + + + + + + + + + 3 + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/dart.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/dart.xml index f1b454fa..4044f491 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/dart.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/dart.xml @@ -81,7 +81,7 @@ - + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/erb.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/erb.xml new file mode 100644 index 00000000..e597cd8a --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/erb.xml @@ -0,0 +1,37 @@ + + + ERB + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gas.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gas.xml index 7557bce0..399cdd00 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gas.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gas.xml @@ -56,7 +56,7 @@ - + @@ -114,7 +114,7 @@ - + @@ -139,7 +139,7 @@ - + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gemfile_lock.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gemfile_lock.xml new file mode 100644 index 00000000..83fc47b7 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gemfile_lock.xml @@ -0,0 +1,81 @@ + + + Gemfile.lock + gemfile-lock + gemfilelock + Gemfile.lock + *.gemfile.lock + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gettext.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gettext.xml new file mode 100644 index 00000000..38c0c21b --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/gettext.xml @@ -0,0 +1,24 @@ + + + + Gettext + pot + po + *.pot + *.po + application/x-gettext + text/x-gettext + text/gettext + + + + + + + + + + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/graphql.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/graphql.xml index b0622735..b40422f2 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/graphql.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/graphql.xml @@ -76,6 +76,10 @@ + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/java.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/java.xml index 3ce33ff6..4ef5fd26 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/java.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/java.xml @@ -32,7 +32,7 @@ pattern="(assert|break|case|catch|continue|default|do|else|finally|for|if|goto|instanceof|new|return|switch|this|throw|try|while)\b"> - + @@ -44,7 +44,7 @@ + pattern="(abstract|const|enum|exports|extends|final|implements|native|non-sealed|open|opens|permits|private|protected|provides|public|requires|sealed|static|strictfp|super|synchronized|throws|to|transient|transitive|uses|volatile|with|yield)\b"> @@ -64,6 +64,10 @@ + + + + @@ -71,7 +75,7 @@ - + @@ -147,6 +151,15 @@ + + + + + + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/javascript.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/javascript.xml index efe80ed3..0e475c53 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/javascript.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/javascript.xml @@ -133,7 +133,7 @@ - + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/json.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/json.xml index a34abfa4..f65d7103 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/json.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/json.xml @@ -2,10 +2,15 @@ JSON json + jsonl *.json + *.jsonl *.jsonc + *.json5 *.avsc + .luaurc application/json + application/jsonl true true diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lateralus.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lateralus.xml new file mode 100644 index 00000000..cea10eba --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lateralus.xml @@ -0,0 +1,184 @@ + + + Lateralus + lateralus + ltl + *.ltl + text/x-lateralus + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lilypond.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lilypond.xml new file mode 100644 index 00000000..fe4ac8ed --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lilypond.xml @@ -0,0 +1,133 @@ + + + + LilyPond + lilypond + *.ly + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lua.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lua.xml index e3d778f1..903d4581 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lua.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/lua.xml @@ -2,10 +2,8 @@ Lua lua - luau *.lua *.wlua - *.luau text/x-lua application/x-lua diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/luau.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/luau.xml new file mode 100644 index 00000000..79a60949 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/luau.xml @@ -0,0 +1,173 @@ + + + Luau + luau + *.luau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/materialize_sql_dialect.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/materialize_sql_dialect.xml index 7094ddc3..616d7ae4 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/materialize_sql_dialect.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/materialize_sql_dialect.xml @@ -45,7 +45,7 @@ - + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/meson.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/meson.xml index 130047df..fcfbda11 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/meson.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/meson.xml @@ -4,6 +4,7 @@ meson meson.build meson.build + meson.options meson_options.txt text/x-meson diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/mysql.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/mysql.xml index b6c2046d..0517ec8f 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/mysql.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/mysql.xml @@ -38,7 +38,7 @@ - + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nu.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nu.xml index 32655808..82d4b375 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nu.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/nu.xml @@ -3,7 +3,12 @@ Nu nu *.nu + application/x-shellscript text/plain + text/x-shellscript + + + @@ -118,4 +123,4 @@ - + \ No newline at end of file diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/php.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/php.xml index c9e22ea5..774bb79b 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/php.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/php.xml @@ -54,7 +54,7 @@ - + @@ -82,12 +82,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -101,6 +135,10 @@ + + + + @@ -116,7 +154,7 @@ - + @@ -132,6 +170,13 @@ + + + + + + + @@ -161,7 +206,7 @@ - + @@ -170,8 +215,9 @@ - + + @@ -185,7 +231,7 @@ - + @@ -194,7 +240,7 @@ - + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/postscript.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/postscript.xml index 15a3422d..32d1bb08 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/postscript.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/postscript.xml @@ -5,6 +5,10 @@ postscr *.ps *.eps + *.epsf + *.epsi + *.pfa + *.t42 application/postscript @@ -86,4 +90,4 @@ - \ No newline at end of file + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/protocol_buffer.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/protocol_buffer.xml index 157d321f..f8f0c8d7 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/protocol_buffer.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/protocol_buffer.xml @@ -7,7 +7,7 @@ - + @@ -16,7 +16,7 @@ - + @@ -34,7 +34,7 @@ - + @@ -46,9 +46,12 @@ - + + + + @@ -62,6 +65,9 @@ + + + @@ -69,7 +75,7 @@ - + @@ -115,4 +121,4 @@ - \ No newline at end of file + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ruby.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ruby.xml index baa7e435..15e96ba8 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ruby.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/ruby.xml @@ -12,7 +12,11 @@ *.rbx *.duby Gemfile + *.gemfile Vagrantfile + Appraisals + .pryrc + *.json.jbuilder text/x-ruby application/x-ruby true diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/scdoc.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/scdoc.xml new file mode 100644 index 00000000..1b3a876e --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/scdoc.xml @@ -0,0 +1,115 @@ + + + scdoc + scdoc + *.scd + *.scdoc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/spade.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/spade.xml new file mode 100644 index 00000000..4dfe3292 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/spade.xml @@ -0,0 +1,292 @@ + + + Spade + spade + *.spade + text/spade + text/x-spade + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/templ.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/templ.xml new file mode 100644 index 00000000..14c26d6b --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/templ.xml @@ -0,0 +1,315 @@ + + + Templ + templ + *.templ + text/x-templ + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/typescript.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/typescript.xml index a3e3be23..b39c9649 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/typescript.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/typescript.xml @@ -226,7 +226,7 @@ diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/xml.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/xml.xml index 2c6a4d99..99d5d530 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/xml.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/xml.xml @@ -10,6 +10,7 @@ *.wsdl *.wsf *.svg + *.qrc *.csproj *.vcxproj *.fsproj diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/yaml.xml b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/yaml.xml index 07075541..ede15d33 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/yaml.xml +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/embedded/yaml.xml @@ -23,6 +23,9 @@ + + + @@ -78,15 +81,22 @@ + + + + + + + - + - + @@ -95,14 +105,14 @@ - + - + diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/erb.go b/vendor/github.com/alecthomas/chroma/v2/lexers/erb.go new file mode 100644 index 00000000..2d141ed9 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/erb.go @@ -0,0 +1,29 @@ +package lexers + +import ( + "strings" + + . "github.com/alecthomas/chroma/v2" // nolint +) + +// ERB lexer is Ruby embedded in HTML. +var ERB = Register(DelegatingLexer(HTML, MustNewXMLLexer( + embedded, + "embedded/erb.xml", +).SetConfig( + &Config{ + Name: "ERB", + Aliases: []string{"erb", "html+erb", "html+ruby", "rhtml"}, + Filenames: []string{"*.erb", "*.html.erb", "*.xml.erb", "*.rhtml"}, + MimeTypes: []string{"application/x-ruby-templating"}, + DotAll: true, + }, +).SetAnalyser(func(text string) float32 { + if strings.Contains(text, "<%=") && strings.Contains(text, "%>") { + return 0.4 + } + if strings.Contains(text, "<%") { + return 0.1 + } + return 0.0 +}))) diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/go.go b/vendor/github.com/alecthomas/chroma/v2/lexers/go.go index c6123b90..e6e04cca 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/go.go +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/go.go @@ -30,8 +30,8 @@ func goRules() Rules { "root": { {`\n`, TextWhitespace, nil}, {`\s+`, TextWhitespace, nil}, - {`//[^\s][^\n\r]*`, CommentPreproc, nil}, - {`//\s+[^\n\r]*`, CommentSingle, nil}, + {`//[^\s\n\r][^\n\r]*`, CommentPreproc, nil}, + {`//[^\n\r]*`, CommentSingle, nil}, {`/(\\\n)?[*](.|\n)*?[*](\\\n)?/`, CommentMultiline, nil}, {`(import|package)\b`, KeywordNamespace, nil}, {`(var|func|struct|map|chan|type|interface|const)\b`, KeywordDeclaration, nil}, diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/http.go b/vendor/github.com/alecthomas/chroma/v2/lexers/http.go index b57cb1b8..fee74d3b 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/http.go +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/http.go @@ -122,7 +122,7 @@ func (d *httpBodyContentTyper) Tokenise(options *TokeniseOptions, text string) ( if err != nil { panic(err) } - return EOF + return subIterator() } } } diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/markdown.go b/vendor/github.com/alecthomas/chroma/v2/lexers/markdown.go index bcd5b178..eed2cb7e 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/markdown.go +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/markdown.go @@ -1,11 +1,13 @@ package lexers import ( + "strings" + . "github.com/alecthomas/chroma/v2" // nolint ) -// Markdown lexer. -var Markdown = Register(MustNewLexer( +// Markdown lexer with YAML frontmatter and HTML comment support. +var Markdown = Register(&markdownLexer{Lexer: MustNewLexer( &Config{ Name: "markdown", Aliases: []string{"md", "mkd"}, @@ -13,11 +15,69 @@ var Markdown = Register(MustNewLexer( MimeTypes: []string{"text/x-markdown"}, }, markdownRules, -)) +)}) + +// markdownLexer wraps the base Markdown lexer to highlight top-of-file YAML frontmatter. +type markdownLexer struct { + Lexer +} + +// Lexes Markdown, highlighting a leading YAML frontmatter block before delegating to Markdown rules. +func (m *markdownLexer) Tokenise(options *TokeniseOptions, text string) (Iterator, error) { + frontmatter, rest, ok := splitFrontmatter(text) + if !ok { + return m.Lexer.Tokenise(options, text) + } + + yamlLexer := Get("YAML") + if yamlLexer == nil { + return m.Lexer.Tokenise(options, text) + } + + yamlTokens, err := yamlLexer.Tokenise(options, frontmatter) + if err != nil { + return nil, err + } + markdownTokens, err := m.Lexer.Tokenise(options, rest) + if err != nil { + return nil, err + } + return Concaterator(yamlTokens, markdownTokens), nil +} + +// Extracts a leading YAML frontmatter block if the document starts with one. +func splitFrontmatter(text string) (frontmatter string, rest string, ok bool) { + if !strings.HasPrefix(text, "---\n") && !strings.HasPrefix(text, "---\r\n") { + return "", text, false + } + + lineEnd := strings.IndexByte(text, '\n') + if lineEnd < 0 { + return "", text, false + } + if strings.TrimSuffix(text[:lineEnd], "\r") != "---" { + return "", text, false + } + + for pos := lineEnd + 1; pos < len(text); { + next := strings.IndexByte(text[pos:], '\n') + if next < 0 { + break + } + lineEnd = pos + next + line := strings.TrimSuffix(text[pos:lineEnd], "\r") + if line == "---" { + return text[:lineEnd+1], text[lineEnd+1:], true + } + pos = lineEnd + 1 + } + return "", text, false +} func markdownRules() Rules { return Rules{ "root": { + {``, CommentMultiline, nil}, {`^(#[^#].+\n)`, ByGroups(GenericHeading), nil}, {`^(#{2,6}.+\n)`, ByGroups(GenericSubheading), nil}, {`^(\s*)([*-] )(\[[ xX]\])( .+\n)`, ByGroups(Text, Keyword, Keyword, UsingSelf("inline")), nil}, @@ -33,6 +93,7 @@ func markdownRules() Rules { Include("inline"), }, "inline": { + {``, CommentMultiline, nil}, {`\\.`, Text, nil}, {`(\s)(\*|_)((?:(?!\2).)*)(\2)((?=\W|\n))`, ByGroups(Text, GenericEmph, GenericEmph, GenericEmph, Text), nil}, {`(\s)((\*\*|__).*?)\3((?=\W|\n))`, ByGroups(Text, GenericStrong, GenericStrong, Text), nil}, diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/markless.go b/vendor/github.com/alecthomas/chroma/v2/lexers/markless.go index 508513d7..437590e9 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/markless.go +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/markless.go @@ -70,14 +70,14 @@ func marklessRules() Rules { {`(! )([^ ]+)(.+?)$`, ByGroups(Keyword, NameFunction, NameVariable), nil}, }, "embed": { - {`(\[ )([^ ]+)( )([^,]+)`, ByGroups(Keyword, NameFunction, TextWhitespace, String), Push("embed-options")}, + {`(\[ )([^ ]+)( )([^,\]\n]+)`, ByGroups(Keyword, NameFunction, TextWhitespace, String), Push("embed-options")}, }, "embed-options": { {`\\.`, Text, nil}, {`,`, Punctuation, nil}, {`\]?$`, Keyword, Pop(1)}, // Generic key or key/value pair - {`( *)([^, \]]+)([^,\]]+)?`, ByGroups(TextWhitespace, NameFunction, String), nil}, + {`( *)([^, \]\n]+)([^,\]\n]+)?`, ByGroups(TextWhitespace, NameFunction, String), nil}, {`.`, Text, nil}, }, "footnote": { diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/raku.go b/vendor/github.com/alecthomas/chroma/v2/lexers/raku.go index da354dce..38d8fe8a 100644 --- a/vendor/github.com/alecthomas/chroma/v2/lexers/raku.go +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/raku.go @@ -6,7 +6,7 @@ import ( "strings" "unicode/utf8" - "github.com/dlclark/regexp2" + "github.com/dlclark/regexp2/v2" . "github.com/alecthomas/chroma/v2" // nolint ) @@ -458,8 +458,7 @@ func rakuRules() Rules { var podRegex *regexp2.Regexp if tokenClass == rakuPod { podRegex = regexp2.MustCompile( - state.NamedGroups[`ws`]+`=end`+`\s+`+regexp2.Escape(state.NamedGroups[`name`]), - 0, + state.NamedGroups[`ws`] + `=end` + `\s+` + regexp2.Escape(state.NamedGroups[`name`]), ) } else { closingChars = []rune(strings.Repeat(string(closingChar), nChars)) @@ -478,7 +477,7 @@ func rakuRules() Rules { match, err := podRegex.FindRunesMatchStartingAt(text, searchPos+nChars) if err == nil { closingChars = match.Runes() - nextClosePos = match.Index + nextClosePos = match.RuneIndex } else { nextClosePos = -1 } @@ -1599,8 +1598,7 @@ func quote(groups []string, state *LexerState) Iterator { var tokenStates []string // Set tokenStates based on adverbs - adverbs := strings.Split(adverbsStr, ":") - for _, adverb := range adverbs { + for adverb := range strings.SplitSeq(adverbsStr, ":") { switch adverb { case "c", "closure": tokenStates = append(tokenStates, "Q-closure") diff --git a/vendor/github.com/alecthomas/chroma/v2/lexers/yaml_jinja.go b/vendor/github.com/alecthomas/chroma/v2/lexers/yaml_jinja.go new file mode 100644 index 00000000..6df1911e --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/lexers/yaml_jinja.go @@ -0,0 +1,20 @@ +package lexers + +import ( + . "github.com/alecthomas/chroma/v2" // nolint +) + +// YAML+Jinja is YAML with Jinja templating embedded. Used by Ansible playbooks +// and Salt SLS files. +var YAMLJinja = Register(DelegatingLexer( + MustNewXMLLexer(embedded, "embedded/yaml.xml"), + MustNewXMLLexer(embedded, "embedded/django_jinja.xml").SetConfig( + &Config{ + Name: "YAML+Jinja", + Aliases: []string{"yaml+jinja", "salt", "sls", "ansible"}, + Filenames: []string{"*.sls"}, + MimeTypes: []string{"text/x-yaml+jinja", "text/x-sls"}, + DotAll: true, + }, + ), +)) diff --git a/vendor/github.com/alecthomas/chroma/v2/regexp.go b/vendor/github.com/alecthomas/chroma/v2/regexp.go index c0e5e108..2c494061 100644 --- a/vendor/github.com/alecthomas/chroma/v2/regexp.go +++ b/vendor/github.com/alecthomas/chroma/v2/regexp.go @@ -3,6 +3,7 @@ package chroma import ( "encoding/json" "fmt" + "maps" "os" "path/filepath" "regexp" @@ -12,7 +13,7 @@ import ( "time" "unicode/utf8" - "github.com/dlclark/regexp2" + "github.com/dlclark/regexp2/v2" ) // A Rule is the fundamental matching unit of the Regex lexer state machine. @@ -70,9 +71,7 @@ func (r Rules) Clone() Rules { // Merge creates a clone of "r" then merges "rules" into the clone. func (r Rules) Merge(rules Rules) Rules { out := r.Clone() - for k, v := range rules.Clone() { - out[k] = v - } + maps.Copy(out, rules.Clone()) return out } @@ -177,19 +176,19 @@ type LexerState struct { // Named Group matches. NamedGroups map[string]string // Custum context for mutators. - MutatorContext map[interface{}]interface{} + MutatorContext map[any]any iteratorStack []Iterator options *TokeniseOptions newlineAdded bool } // Set mutator context. -func (l *LexerState) Set(key interface{}, value interface{}) { +func (l *LexerState) Set(key any, value any) { l.MutatorContext[key] = value } // Get mutator context. -func (l *LexerState) Get(key interface{}) interface{} { +func (l *LexerState) Get(key any) any { return l.MutatorContext[key] } @@ -308,6 +307,7 @@ type RegexLexer struct { rules map[string][]*CompiledRule fetchRulesFunc func() (Rules, error) compileOnce sync.Once + compileError error } func (r *RegexLexer) String() string { @@ -368,7 +368,7 @@ func (r *RegexLexer) maybeCompile() (err error) { pattern = "(?" + rule.flags + ")" + pattern } pattern = `\G` + pattern - rule.Regexp, err = regexp2.Compile(pattern, 0) + rule.Regexp, err = regexp2.Compile(pattern) if err != nil { return fmt.Errorf("failed to compile rule %s.%d: %s", state, i, err) } @@ -446,8 +446,11 @@ func (r *RegexLexer) needRules() error { var err error if r.fetchRulesFunc != nil { r.compileOnce.Do(func() { - err = r.fetchRules() + r.compileError = r.fetchRules() }) + if r.compileError != nil { + return r.compileError + } } if err := r.maybeCompile(); err != nil { return err @@ -480,7 +483,7 @@ func (r *RegexLexer) Tokenise(options *TokeniseOptions, text string) (Iterator, Text: []rune(text), Stack: []string{options.State}, Rules: r.rules, - MutatorContext: map[interface{}]interface{}{}, + MutatorContext: map[any]any{}, } return state.Iterator, nil } @@ -497,7 +500,7 @@ func (r *RegexLexer) MustRules() Rules { func matchRules(text []rune, pos int, rules []*CompiledRule) (int, *CompiledRule, []string, map[string]string) { for i, rule := range rules { match, err := rule.Regexp.FindRunesMatchStartingAt(text, pos) - if match != nil && err == nil && match.Index == pos { + if match != nil && err == nil && match.RuneIndex == pos { groups := []string{} namedGroups := make(map[string]string) for _, g := range match.Groups() { diff --git a/vendor/github.com/alecthomas/chroma/v2/renovate.json5 b/vendor/github.com/alecthomas/chroma/v2/renovate.json5 index 9ade4812..050483e8 100644 --- a/vendor/github.com/alecthomas/chroma/v2/renovate.json5 +++ b/vendor/github.com/alecthomas/chroma/v2/renovate.json5 @@ -9,6 +9,7 @@ "schedule:earlyMondays", // Run once a week. 'helpers:pinGitHubActionDigests', ], + "postUpdateOptions": ["gomodTidy"], "packageRules": [ { "matchPackageNames": ["golangci-lint"], diff --git a/vendor/github.com/alecthomas/chroma/v2/serialise.go b/vendor/github.com/alecthomas/chroma/v2/serialise.go index 3d7f6c5d..dacf69c5 100644 --- a/vendor/github.com/alecthomas/chroma/v2/serialise.go +++ b/vendor/github.com/alecthomas/chroma/v2/serialise.go @@ -13,7 +13,7 @@ import ( "regexp" "strings" - "github.com/dlclark/regexp2" + "github.com/dlclark/regexp2/v2" ) // Serialisation of Chroma rules to XML. The format is: @@ -440,14 +440,14 @@ func (t TokenType) MarshalXML(e *xml.Encoder, start xml.StartElement) error { } // This hijinks is a bit unfortunate but without it we can't deserialise into TokenType. -func newFromTemplate(template interface{}) (value func() interface{}, target interface{}) { +func newFromTemplate(template any) (value func() any, target any) { t := reflect.TypeOf(template) - if t.Kind() == reflect.Ptr { + if t.Kind() == reflect.Pointer { v := reflect.New(t.Elem()) return v.Interface, v.Interface() } v := reflect.New(t) - return func() interface{} { return v.Elem().Interface() }, v.Interface() + return func() any { return v.Elem().Interface() }, v.Interface() } func (b *Emitters) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { diff --git a/vendor/github.com/alecthomas/chroma/v2/style.go b/vendor/github.com/alecthomas/chroma/v2/style.go index cc8d9a60..36989ad3 100644 --- a/vendor/github.com/alecthomas/chroma/v2/style.go +++ b/vendor/github.com/alecthomas/chroma/v2/style.go @@ -4,7 +4,8 @@ import ( "encoding/xml" "fmt" "io" - "sort" + "maps" + "slices" "strings" ) @@ -18,6 +19,24 @@ const ( No ) +// Mode indicates whether a style is intended for a light or dark background. +type Mode uint8 + +// Mode values. +const ( + Light Mode = iota + Dark +) + +func (m Mode) String() string { + switch m { + case Dark: + return "dark" + default: + return "light" + } +} + func (t Trilean) String() string { switch t { case Yes: @@ -31,12 +50,14 @@ func (t Trilean) String() string { // Prefix returns s with "no" as a prefix if Trilean is no. func (t Trilean) Prefix(s string) string { - if t == Yes { + switch t { + case Yes: return s - } else if t == No { + case No: return "no" + s + default: + return "" } - return "" } // A StyleEntry in the Style map. @@ -111,11 +132,10 @@ func (s StyleEntry) Sub(e StyleEntry) StyleEntry { // Ancestors should be provided from oldest to newest. func (s StyleEntry) Inherit(ancestors ...StyleEntry) StyleEntry { out := s - for i := len(ancestors) - 1; i >= 0; i-- { + for _, ancestor := range slices.Backward(ancestors) { if out.NoInherit { return out } - ancestor := ancestors[i] if !out.Colour.IsSet() { out.Colour = ancestor.Colour } @@ -147,19 +167,24 @@ func (s StyleEntry) IsZero() bool { // // Once built, a Style is immutable. type StyleBuilder struct { - entries map[TokenType]string - name string - parent *Style + entries map[TokenType]string + name string + counterpart string + parent *Style } func NewStyleBuilder(name string) *StyleBuilder { return &StyleBuilder{name: name, entries: map[TokenType]string{}} } +// Counterpart sets the lowercase name of the opposite-mode style. +func (s *StyleBuilder) Counterpart(name string) *StyleBuilder { + s.counterpart = strings.ToLower(name) + return s +} + func (s *StyleBuilder) AddAll(entries StyleEntries) *StyleBuilder { - for ttype, entry := range entries { - s.entries[ttype] = entry - } + maps.Copy(s.entries, entries) return s } @@ -205,10 +230,15 @@ func (s *StyleBuilder) Transform(transform func(StyleEntry) StyleEntry) *StyleBu } func (s *StyleBuilder) Build() (*Style, error) { + counterpart := s.counterpart + if counterpart == "" && s.parent != nil { + counterpart = s.parent.Counterpart + } style := &Style{ - Name: s.name, - entries: map[TokenType]StyleEntry{}, - parent: s.parent, + Name: s.name, + Counterpart: counterpart, + entries: map[TokenType]StyleEntry{}, + parent: s.parent, } for ttype, descriptor := range s.entries { entry, err := ParseStyleEntry(descriptor) @@ -257,9 +287,23 @@ func MustNewStyle(name string, entries StyleEntries) *Style { // // See http://pygments.org/docs/styles/ for details. Semantics are intended to be identical. type Style struct { - Name string - entries map[TokenType]StyleEntry - parent *Style + Name string + // Counterpart is the lowercase name of the style intended as this style's + // opposite-mode pair (eg. "github-dark" for "github"). Resolved via + // styles.GetForMode. May be empty. + Counterpart string + entries map[TokenType]StyleEntry + parent *Style +} + +// Mode returns Light or Dark based on the brightness of the Background entry's +// background colour. Styles with an unset Background default to Light. +func (s *Style) Mode() Mode { + bg := s.get(Background).Background + if bg.IsSet() && bg.Brightness() < 0.5 { + return Dark + } + return Light } func (s *Style) MarshalXML(e *xml.Encoder, start xml.StartElement) error { @@ -268,6 +312,9 @@ func (s *Style) MarshalXML(e *xml.Encoder, start xml.StartElement) error { } start.Name = xml.Name{Local: "style"} start.Attr = []xml.Attr{{Name: xml.Name{Local: "name"}, Value: s.Name}} + if s.Counterpart != "" { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "counterpart"}, Value: s.Counterpart}) + } if err := e.EncodeToken(start); err != nil { return err } @@ -275,7 +322,7 @@ func (s *Style) MarshalXML(e *xml.Encoder, start xml.StartElement) error { for ttype := range s.entries { sorted = append(sorted, ttype) } - sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] }) + slices.Sort(sorted) for _, ttype := range sorted { entry := s.entries[ttype] el := xml.StartElement{Name: xml.Name{Local: "entry"}} @@ -295,9 +342,12 @@ func (s *Style) MarshalXML(e *xml.Encoder, start xml.StartElement) error { func (s *Style) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { for _, attr := range start.Attr { - if attr.Name.Local == "name" { + switch attr.Name.Local { + case "name": s.Name = attr.Value - } else { + case "counterpart": + s.Counterpart = strings.ToLower(attr.Value) + default: return fmt.Errorf("unexpected attribute %s", attr.Name.Local) } } @@ -437,8 +487,7 @@ func MustParseStyleEntry(entry string) StyleEntry { // ParseStyleEntry parses a Pygments style entry. func ParseStyleEntry(entry string) (StyleEntry, error) { // nolint: gocyclo out := StyleEntry{} - parts := strings.Fields(entry) - for _, part := range parts { + for part := range strings.FieldsSeq(entry) { switch { case part == "italic": out.Italic = Yes diff --git a/vendor/github.com/alecthomas/chroma/v2/styles/api.go b/vendor/github.com/alecthomas/chroma/v2/styles/api.go index e26d6f0a..43fa26dd 100644 --- a/vendor/github.com/alecthomas/chroma/v2/styles/api.go +++ b/vendor/github.com/alecthomas/chroma/v2/styles/api.go @@ -4,6 +4,7 @@ import ( "embed" "io/fs" "sort" + "strings" "github.com/alecthomas/chroma/v2" ) @@ -31,7 +32,7 @@ var Registry = func() map[string]*chroma.Style { if err != nil { panic(err) } - registry[style.Name] = style + registry[strings.ToLower(style.Name)] = style _ = r.Close() } return registry @@ -42,7 +43,7 @@ var Fallback = Registry["swapoff"] // Register a chroma.Style. func Register(style *chroma.Style) *chroma.Style { - Registry[style.Name] = style + Registry[strings.ToLower(style.Name)] = style return style } @@ -58,8 +59,37 @@ func Names() []string { // Get named style, or Fallback. func Get(name string) *chroma.Style { - if style, ok := Registry[name]; ok { + if style, ok := Registry[strings.ToLower(name)]; ok { return style } return Fallback } + +// GetForMode returns the named style if it already matches mode, otherwise its +// registered counterpart if one exists and matches mode. If neither matches, +// the originally-requested style is returned (or Fallback if the name is +// unknown), so callers always get something usable. +func GetForMode(name string, mode chroma.Mode) *chroma.Style { + style := Get(name) + if style.Mode() == mode { + return style + } + if style.Counterpart == "" { + return style + } + counterpart, ok := Registry[style.Counterpart] + if !ok || counterpart.Mode() != mode { + return style + } + return counterpart +} + +// RegisterPair links two styles as light/dark counterparts of each other. +// +// Both styles are also registered if they are not already present. +func RegisterPair(a, b *chroma.Style) { + Register(a) + Register(b) + a.Counterpart = strings.ToLower(b.Name) + b.Counterpart = strings.ToLower(a.Name) +} diff --git a/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-latte.xml b/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-latte.xml index c87c8765..a6e41ab8 100644 --- a/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-latte.xml +++ b/vendor/github.com/alecthomas/chroma/v2/styles/catppuccin-latte.xml @@ -1,4 +1,4 @@ - \ No newline at end of file diff --git a/vendor/github.com/alecthomas/chroma/v2/styles/github-dark.xml b/vendor/github.com/alecthomas/chroma/v2/styles/github-dark.xml index 711aeafc..9f2b7e14 100644 --- a/vendor/github.com/alecthomas/chroma/v2/styles/github-dark.xml +++ b/vendor/github.com/alecthomas/chroma/v2/styles/github-dark.xml @@ -1,4 +1,4 @@ - diff --git a/vendor/github.com/alecthomas/chroma/v2/styles/kanagawa-lotus.xml b/vendor/github.com/alecthomas/chroma/v2/styles/kanagawa-lotus.xml new file mode 100644 index 00000000..622ae4e4 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/styles/kanagawa-lotus.xml @@ -0,0 +1,83 @@ + diff --git a/vendor/github.com/alecthomas/chroma/v2/styles/kanagawa-wave.xml b/vendor/github.com/alecthomas/chroma/v2/styles/kanagawa-wave.xml new file mode 100644 index 00000000..bc9b6461 --- /dev/null +++ b/vendor/github.com/alecthomas/chroma/v2/styles/kanagawa-wave.xml @@ -0,0 +1,83 @@ + diff --git a/vendor/github.com/alecthomas/chroma/v2/styles/modus-operandi.xml b/vendor/github.com/alecthomas/chroma/v2/styles/modus-operandi.xml index 023137aa..066efa7c 100644 --- a/vendor/github.com/alecthomas/chroma/v2/styles/modus-operandi.xml +++ b/vendor/github.com/alecthomas/chroma/v2/styles/modus-operandi.xml @@ -1,4 +1,4 @@ - `) diff --git a/vendor/github.com/golangci/dupl/suffixtree/dupl.go b/vendor/github.com/golangci/dupl/suffixtree/dupl.go index ab145b4f..0263203e 100644 --- a/vendor/github.com/golangci/dupl/suffixtree/dupl.go +++ b/vendor/github.com/golangci/dupl/suffixtree/dupl.go @@ -54,7 +54,7 @@ func (c *contextList) append(c2 *contextList) { } } -// FindDuplOver find pairs of maximal duplicities over a threshold +// FindDuplOver finds pairs of maximal duplicities over a threshold // length. func (t *STree) FindDuplOver(threshold int) <-chan Match { auxTran := newTran(0, 0, t.root) diff --git a/vendor/github.com/golangci/dupl/suffixtree/suffixtree.go b/vendor/github.com/golangci/dupl/suffixtree/suffixtree.go index 871469e8..30aaa6f4 100644 --- a/vendor/github.com/golangci/dupl/suffixtree/suffixtree.go +++ b/vendor/github.com/golangci/dupl/suffixtree/suffixtree.go @@ -38,7 +38,7 @@ func New() *STree { return t } -// Update refreshes the suffix tree to by new data. +// Update refreshes the suffix tree with new data. func (t *STree) Update(data ...Token) { t.data = append(t.data, data...) for range data { @@ -79,8 +79,8 @@ func (t *STree) update() { } // testAndSplit tests whether a state with canonical ref. pair -// (s, (start, end)) is the end point, that is, a state that have -// a c-transition. If not, then state (exs, (start, end)) is made +// (s, (start, end)) is the end point, that is, a state that has +// a c-transition. If not, then the state (exs, (start, end)) is made // explicit (if not already so). func (t *STree) testAndSplit(s *state, start, end Pos) (exs *state, endPoint bool) { c := t.data[t.end] @@ -148,11 +148,11 @@ func (t *STree) String() string { return buf.String() } -func printState(buf *bytes.Buffer, s *state, ident int) { +func printState(buf *bytes.Buffer, s *state, indent int) { for _, tr := range s.trans { - fmt.Fprint(buf, strings.Repeat(" ", ident)) + fmt.Fprint(buf, strings.Repeat(" ", indent)) fmt.Fprintf(buf, "* (%d, %d)\n", tr.start, tr.ActEnd()) - printState(buf, tr.state, ident+1) + printState(buf, tr.state, indent+1) } } diff --git a/vendor/github.com/golangci/dupl/syntax/golang/golang.go b/vendor/github.com/golangci/dupl/syntax/golang/golang.go index a0b1e77e..ff3e51b2 100644 --- a/vendor/github.com/golangci/dupl/syntax/golang/golang.go +++ b/vendor/github.com/golangci/dupl/syntax/golang/golang.go @@ -39,6 +39,7 @@ const ( IfStmt IncDecStmt IndexExpr + IndexListExpr InterfaceType KeyValueExpr LabeledStmt @@ -234,6 +235,9 @@ func (t *transformer) trans(node ast.Node) (o *syntax.Node) { case *ast.FuncType: o.Type = FuncType + if n.TypeParams != nil { + o.AddChildren(t.trans(n.TypeParams)) + } o.AddChildren(t.trans(n.Params)) if n.Results != nil { o.AddChildren(t.trans(n.Results)) @@ -270,6 +274,13 @@ func (t *transformer) trans(node ast.Node) (o *syntax.Node) { o.Type = IndexExpr o.AddChildren(t.trans(n.X), t.trans(n.Index)) + case *ast.IndexListExpr: + o.Type = IndexListExpr + o.AddChildren(t.trans(n.X)) + for _, idx := range n.Indices { + o.AddChildren(t.trans(idx)) + } + case *ast.InterfaceType: o.Type = InterfaceType o.AddChildren(t.trans(n.Methods)) @@ -358,7 +369,11 @@ func (t *transformer) trans(node ast.Node) (o *syntax.Node) { case *ast.TypeSpec: o.Type = TypeSpec - o.AddChildren(t.trans(n.Name), t.trans(n.Type)) + o.AddChildren(t.trans(n.Name)) + if n.TypeParams != nil { + o.AddChildren(t.trans(n.TypeParams)) + } + o.AddChildren(t.trans(n.Type)) case *ast.TypeSwitchStmt: o.Type = TypeSwitchStmt diff --git a/vendor/github.com/golangci/dupl/syntax/syntax.go b/vendor/github.com/golangci/dupl/syntax/syntax.go index 9b11d311..871f6718 100644 --- a/vendor/github.com/golangci/dupl/syntax/syntax.go +++ b/vendor/github.com/golangci/dupl/syntax/syntax.go @@ -129,8 +129,8 @@ func getUnitsIndexes(nodeSeq []*Node, threshold int) []int { return indexes } -// isCyclic finds out whether there is a repetive pattern in the found clone. If positive, -// it return false to point out that the clone would be redundant. +// isCyclic finds out whether there is a repetitive pattern in the found clone. If positive, +// it returns true to point out that the clone would be redundant. func isCyclic(indexes []int, nodes []*Node) bool { cnt := len(indexes) if cnt <= 1 { diff --git a/vendor/github.com/golangci/gofmt/Makefile b/vendor/github.com/golangci/gofmt/Makefile new file mode 100644 index 00000000..8375bf40 --- /dev/null +++ b/vendor/github.com/golangci/gofmt/Makefile @@ -0,0 +1,9 @@ +.PHONY: build + +default: test build + +build: + go build . + +test: + go test ./... diff --git a/vendor/github.com/golangci/gofmt/gofmt.go b/vendor/github.com/golangci/gofmt/gofmt.go new file mode 100644 index 00000000..430a1b8d --- /dev/null +++ b/vendor/github.com/golangci/gofmt/gofmt.go @@ -0,0 +1,73 @@ +package gofmt + +import ( + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + + "github.com/golangci/gofmt/internal" +) + +type Options struct { + NeedSimplify bool + RewriteRules []RewriteRule +} + +type RewriteRule struct { + Pattern string + Replacement string +} + +// Source formats the code like gofmt. +// Empty string `rewrite` will be ignored. +// https://github.com/golang/go/blob/1b291b70dff51732415da5b68debe323704d8e8d/src/cmd/gofmt/gofmt.go#L236-L300 +// https://github.com/golang/go/blob/1b291b70dff51732415da5b68debe323704d8e8d/src/go/format/format.go#L101-L115 +func Source(filename string, src []byte, opts Options) ([]byte, error) { + fset := token.NewFileSet() + + file, sourceAdj, indentAdj, err := internal.Parse(fset, filename, src, false) + if err != nil { + return nil, err + } + + file, err = rewriteFileContent(fset, file, opts.RewriteRules) + if err != nil { + return nil, err + } + + ast.SortImports(fset, file) + + if opts.NeedSimplify { + internal.Simplify(file) + } + + return internal.Format(fset, file, sourceAdj, indentAdj, src, printer.Config{Mode: internal.PrinterMode, Tabwidth: internal.TabWidth}) +} + +func rewriteFileContent(fset *token.FileSet, file *ast.File, rewriteRules []RewriteRule) (*ast.File, error) { + for _, rewriteRule := range rewriteRules { + pattern, err := parseExpression(rewriteRule.Pattern, "pattern") + if err != nil { + return nil, err + } + + replacement, err := parseExpression(rewriteRule.Replacement, "replacement") + if err != nil { + return nil, err + } + + file = internal.RewriteFile(fset, pattern, replacement, file) + } + + return file, nil +} + +func parseExpression(s, what string) (ast.Expr, error) { + x, err := parser.ParseExpr(s) + if err != nil { + return nil, fmt.Errorf("parsing %s %q at %s\n", what, s, err) + } + return x, nil +} diff --git a/vendor/github.com/golangci/gofmt/gofmt/golangci.go b/vendor/github.com/golangci/gofmt/gofmt/golangci.go deleted file mode 100644 index a7f3ef6e..00000000 --- a/vendor/github.com/golangci/gofmt/gofmt/golangci.go +++ /dev/null @@ -1,133 +0,0 @@ -package gofmt - -import ( - "bytes" - "fmt" - "go/ast" - "go/parser" - "go/printer" - "go/token" - "os" - "path/filepath" - "sync" - - "github.com/rogpeppe/go-internal/diff" -) - -type Options struct { - NeedSimplify bool - RewriteRules []RewriteRule -} - -var parserModeMu sync.RWMutex - -type RewriteRule struct { - Pattern string - Replacement string -} - -// Run runs gofmt. -// Deprecated: use [Source] instead. -func Run(filename string, needSimplify bool) ([]byte, error) { - return RunRewrite(filename, needSimplify, nil) -} - -// RunRewrite runs gofmt. -// Deprecated: use [Source] instead. -func RunRewrite(filename string, needSimplify bool, rewriteRules []RewriteRule) ([]byte, error) { - src, err := os.ReadFile(filename) - if err != nil { - return nil, err - } - - fset := token.NewFileSet() - - parserModeMu.Lock() - initParserMode() - parserModeMu.Unlock() - - file, sourceAdj, indentAdj, err := parse(fset, filename, src, false) - if err != nil { - return nil, err - } - - file, err = rewriteFileContent(fset, file, rewriteRules) - if err != nil { - return nil, err - } - - ast.SortImports(fset, file) - - if needSimplify { - simplify(file) - } - - res, err := format(fset, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth}) - if err != nil { - return nil, err - } - - if bytes.Equal(src, res) { - return nil, nil - } - - // formatting has changed - newName := filepath.ToSlash(filename) - oldName := newName + ".orig" - - return diff.Diff(oldName, src, newName, res), nil -} - -// Source formats the code like gofmt. -// Empty string `rewrite` will be ignored. -func Source(filename string, src []byte, opts Options) ([]byte, error) { - fset := token.NewFileSet() - - parserModeMu.Lock() - initParserMode() - parserModeMu.Unlock() - - file, sourceAdj, indentAdj, err := parse(fset, filename, src, false) - if err != nil { - return nil, err - } - - file, err = rewriteFileContent(fset, file, opts.RewriteRules) - if err != nil { - return nil, err - } - - ast.SortImports(fset, file) - - if opts.NeedSimplify { - simplify(file) - } - - return format(fset, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth}) -} - -func rewriteFileContent(fset *token.FileSet, file *ast.File, rewriteRules []RewriteRule) (*ast.File, error) { - for _, rewriteRule := range rewriteRules { - pattern, err := parseExpression(rewriteRule.Pattern, "pattern") - if err != nil { - return nil, err - } - - replacement, err := parseExpression(rewriteRule.Replacement, "replacement") - if err != nil { - return nil, err - } - - file = rewriteFile(fset, pattern, replacement, file) - } - - return file, nil -} - -func parseExpression(s, what string) (ast.Expr, error) { - x, err := parser.ParseExpr(s) - if err != nil { - return nil, fmt.Errorf("parsing %s %q at %s\n", what, s, err) - } - return x, nil -} diff --git a/vendor/github.com/golangci/gofmt/gofmt/readme.md b/vendor/github.com/golangci/gofmt/gofmt/readme.md deleted file mode 100644 index d11d4641..00000000 --- a/vendor/github.com/golangci/gofmt/gofmt/readme.md +++ /dev/null @@ -1,18 +0,0 @@ -# Hard Fork of gofmt - -- https://github.com/golang/go/blob/master/src/cmd/gofmt/ -- https://github.com/golang/go/blob/master/src/internal/cfg -- https://github.com/golang/go/blob/master/src/internal/goarch -- https://github.com/golang/go/blob/master/src/internal/testenv -- https://github.com/golang/go/blob/master/src/internal/platform -- https://github.com/golang/go/blob/master/src/internal/diff -> replaced by `github.com/rogpeppe/go-internal/diff` - -## Updates - -- 2025-07-04: Sync with go1.24.4 -- 2025-04-14: Sync with go1.23.8 -- 2024-08-17: Sync with go1.22.6 -- 2023-02-28: Sync with go1.21.7 -- 2023-10-04: Sync with go1.20.8 -- 2023-10-04: Sync with go1.19.13 -- 2022-08-31: Sync with go1.18.5 diff --git a/vendor/github.com/golangci/gofmt/internal/.gitattributes b/vendor/github.com/golangci/gofmt/internal/.gitattributes new file mode 100644 index 00000000..19d80d28 --- /dev/null +++ b/vendor/github.com/golangci/gofmt/internal/.gitattributes @@ -0,0 +1 @@ +/testdata/crlf.input text eol=crlf diff --git a/vendor/github.com/golangci/gofmt/gofmt/LICENSE b/vendor/github.com/golangci/gofmt/internal/LICENSE similarity index 100% rename from vendor/github.com/golangci/gofmt/gofmt/LICENSE rename to vendor/github.com/golangci/gofmt/internal/LICENSE diff --git a/vendor/github.com/golangci/gofmt/gofmt/doc.go b/vendor/github.com/golangci/gofmt/internal/doc.go similarity index 99% rename from vendor/github.com/golangci/gofmt/gofmt/doc.go rename to vendor/github.com/golangci/gofmt/internal/doc.go index d0a45802..6a25f1de 100644 --- a/vendor/github.com/golangci/gofmt/gofmt/doc.go +++ b/vendor/github.com/golangci/gofmt/internal/doc.go @@ -99,7 +99,7 @@ When invoked with -s gofmt will make the following source transformations where This may result in changes that are incompatible with earlier versions of Go. */ -package gofmt +package internal // BUG(rsc): The implementation of -r is a bit slow. // BUG(gri): If -w fails, the restored original file may not have some of the diff --git a/vendor/github.com/golangci/gofmt/gofmt/gofmt.go b/vendor/github.com/golangci/gofmt/internal/gofmt.go similarity index 89% rename from vendor/github.com/golangci/gofmt/gofmt/gofmt.go rename to vendor/github.com/golangci/gofmt/internal/gofmt.go index f4543582..1fd62b8f 100644 --- a/vendor/github.com/golangci/gofmt/gofmt/gofmt.go +++ b/vendor/github.com/golangci/gofmt/internal/gofmt.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package gofmt +package internal import ( "bytes" @@ -10,7 +10,6 @@ import ( "flag" "fmt" "go/ast" - "go/parser" "go/printer" "go/scanner" "go/token" @@ -39,6 +38,9 @@ var ( // debugging cpuprofile = flag.String("gofmt.cpuprofile", "", "write cpu profile to this file") + + // errors + errFormattingDiffers = fmt.Errorf("formatting differs from gofmt's") ) // Keep these in sync with go/format/format.go. @@ -64,8 +66,8 @@ const ( var fdSem = make(chan bool, 200) var ( - rewrite func(*token.FileSet, *ast.File) *ast.File - parserMode parser.Mode + rewrite func(*token.FileSet, *ast.File) *ast.File + // parserMode parser.Mode // NOTE(golangci-lint): replaced with a constant ) func usage() { @@ -73,22 +75,23 @@ func usage() { flag.PrintDefaults() } +// NOTE(golangci-lint): replaced with a constant func initParserMode() { - parserMode = parser.ParseComments - if *allErrors { - parserMode |= parser.AllErrors - } - // It's only -r that makes use of go/ast's object resolution, - // so avoid the unnecessary work if the flag isn't used. - if *rewriteRule == "" { - parserMode |= parser.SkipObjectResolution - } + /* + parserMode = parser.ParseComments + if *allErrors { + parserMode |= parser.AllErrors + } + // It's only -r that makes use of go/ast's object resolution, + // so avoid the unnecessary work if the flag isn't used. + if *rewriteRule == "" { + parserMode |= parser.SkipObjectResolution + } + */ } -func isGoFile(f fs.DirEntry) bool { - // ignore non-Go files - name := f.Name() - return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && !f.IsDir() +func isGoFilename(name string) bool { + return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") } // A sequencer performs concurrent tasks that may write output, but emits that @@ -218,8 +221,12 @@ func (r *reporter) Report(err error) { panic("Report with nil error") } st := r.getState() - scanner.PrintError(st.err, err) - st.exitCode = 2 + if err == errFormattingDiffers { + st.exitCode = 1 + } else { + scanner.PrintError(st.err, err) + st.exitCode = 2 + } } func (r *reporter) ExitCode() int { @@ -273,7 +280,7 @@ func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) e } perm := info.Mode().Perm() - if err := writeFile(filename, src, res, perm, info.Size()); err != nil { + if err := writeFile(filename, src, res, perm); err != nil { return err } } @@ -281,6 +288,7 @@ func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) e newName := filepath.ToSlash(filename) oldName := newName + ".orig" r.Write(diff.Diff(oldName, src, newName, res)) + return errFormattingDiffers } } @@ -406,34 +414,30 @@ func gofmtMain(s *sequencer) { } for _, arg := range args { - switch info, err := os.Stat(arg); { - case err != nil: - s.AddReport(err) - case !info.IsDir(): - // Non-directory arguments are always formatted. - arg := arg - s.Add(fileWeight(arg, info), func(r *reporter) error { - return processFile(arg, info, nil, r) - }) - default: - // Directories are walked, ignoring non-Go files. - err := filepath.WalkDir(arg, func(path string, f fs.DirEntry, err error) error { - if err != nil || !isGoFile(f) { - return err - } - info, err := f.Info() - if err != nil { - s.AddReport(err) - return nil - } - s.Add(fileWeight(path, info), func(r *reporter) error { - return processFile(path, info, nil, r) - }) - return nil - }) + // Walk each given argument as a directory tree. + // If the argument is not a directory, it's always formatted as a Go file. + // If the argument is a directory, we walk it, ignoring non-Go files. + if err := filepath.WalkDir(arg, func(path string, d fs.DirEntry, err error) error { + switch { + case err != nil: + return err + case d.IsDir(): + return nil // simply recurse into directories + case path == arg: + // non-directories given as explicit arguments are always formatted + case !isGoFilename(d.Name()): + return nil // skip walked non-Go files + } + info, err := d.Info() if err != nil { - s.AddReport(err) + return err } + s.Add(fileWeight(path, info), func(r *reporter) error { + return processFile(path, info, nil, r) + }) + return nil + }); err != nil { + s.AddReport(err) } } } @@ -458,7 +462,7 @@ func fileWeight(path string, info fs.FileInfo) int64 { } // writeFile updates a file with the new formatted data. -func writeFile(filename string, orig, formatted []byte, perm fs.FileMode, size int64) error { +func writeFile(filename string, orig, formatted []byte, perm fs.FileMode) error { // Make a temporary backup file before rewriting the original file. bakname, err := backupFile(filename, orig, perm) if err != nil { @@ -482,7 +486,7 @@ func writeFile(filename string, orig, formatted []byte, perm fs.FileMode, size i } n, err := fout.Write(formatted) - if err == nil && int64(n) < size { + if err == nil { err = fout.Truncate(int64(n)) } diff --git a/vendor/github.com/golangci/gofmt/internal/golangci.go b/vendor/github.com/golangci/gofmt/internal/golangci.go new file mode 100644 index 00000000..b05e5aaf --- /dev/null +++ b/vendor/github.com/golangci/gofmt/internal/golangci.go @@ -0,0 +1,51 @@ +package internal + +import ( + "go/ast" + "go/parser" + "go/printer" + "go/token" +) + +/* + The goal of this file is: + - to expose the unexported constants and the function. + - set the parserMode. +*/ + +const ( + TabWidth = tabWidth + PrinterMode = printerMode +) + +// https://github.com/golang/go/blob/1b291b70dff51732415da5b68debe323704d8e8d/src/cmd/gofmt/gofmt.go#L81-L91 +// https://github.com/golang/go/blob/1b291b70dff51732415da5b68debe323704d8e8d/src/go/format/format.go#L41 +const parserMode = parser.ParseComments | parser.SkipObjectResolution + +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, +) { + return parse(fset, filename, src, fragmentOk) +} + +func Format( + fset *token.FileSet, + file *ast.File, + sourceAdj func(src []byte, indent int) []byte, + indentAdj int, + src []byte, + cfg printer.Config, +) ([]byte, error) { + return format(fset, file, sourceAdj, indentAdj, src, cfg) +} + +func Simplify(f *ast.File) { + simplify(f) +} + +func RewriteFile(fileSet *token.FileSet, pattern, replace ast.Expr, p *ast.File) *ast.File { + return rewriteFile(fileSet, pattern, replace, p) +} diff --git a/vendor/github.com/golangci/gofmt/gofmt/internal.go b/vendor/github.com/golangci/gofmt/internal/internal.go similarity index 96% rename from vendor/github.com/golangci/gofmt/gofmt/internal.go rename to vendor/github.com/golangci/gofmt/internal/internal.go index 231a2509..ef60a143 100644 --- a/vendor/github.com/golangci/gofmt/gofmt/internal.go +++ b/vendor/github.com/golangci/gofmt/internal/internal.go @@ -7,7 +7,7 @@ // one without the other. Determine if we can factor out functionality // in a public API. See also #11844 for context. -package gofmt +package internal import ( "bytes" @@ -26,13 +26,6 @@ func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( indentAdj int, err error, ) { - - // START - Change related to usage inside golangci-lint - parserModeMu.Lock() - parserMode := parserMode - parserModeMu.Unlock() - // END - Change related to usage inside golangci-lint - // 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 diff --git a/vendor/github.com/golangci/gofmt/gofmt/rewrite.go b/vendor/github.com/golangci/gofmt/internal/rewrite.go similarity index 96% rename from vendor/github.com/golangci/gofmt/gofmt/rewrite.go rename to vendor/github.com/golangci/gofmt/internal/rewrite.go index c95d44f6..0a399986 100644 --- a/vendor/github.com/golangci/gofmt/gofmt/rewrite.go +++ b/vendor/github.com/golangci/gofmt/internal/rewrite.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package gofmt +package internal import ( "fmt" @@ -105,11 +105,11 @@ var ( objectPtrNil = reflect.ValueOf((*ast.Object)(nil)) scopePtrNil = reflect.ValueOf((*ast.Scope)(nil)) - identType = reflect.TypeOf((*ast.Ident)(nil)) - objectPtrType = reflect.TypeOf((*ast.Object)(nil)) - positionType = reflect.TypeOf(token.NoPos) - callExprType = reflect.TypeOf((*ast.CallExpr)(nil)) - scopePtrType = reflect.TypeOf((*ast.Scope)(nil)) + identType = reflect.TypeFor[*ast.Ident]() + objectPtrType = reflect.TypeFor[*ast.Object]() + positionType = reflect.TypeFor[token.Pos]() + callExprType = reflect.TypeFor[*ast.CallExpr]() + scopePtrType = reflect.TypeFor[*ast.Scope]() ) // apply replaces each AST field x in val with f(x), returning val. diff --git a/vendor/github.com/golangci/gofmt/gofmt/simplify.go b/vendor/github.com/golangci/gofmt/internal/simplify.go similarity index 99% rename from vendor/github.com/golangci/gofmt/gofmt/simplify.go rename to vendor/github.com/golangci/gofmt/internal/simplify.go index 3b34d562..d57e8cef 100644 --- a/vendor/github.com/golangci/gofmt/gofmt/simplify.go +++ b/vendor/github.com/golangci/gofmt/internal/simplify.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package gofmt +package internal import ( "go/ast" diff --git a/vendor/github.com/golangci/gofmt/readme.md b/vendor/github.com/golangci/gofmt/readme.md new file mode 100644 index 00000000..6e1b0036 --- /dev/null +++ b/vendor/github.com/golangci/gofmt/readme.md @@ -0,0 +1,46 @@ +# Hard Fork of gofmt + +## Updates + +- 2026-08-20: Sync with go1.27.0 +- 2025-12-14: Sync with go1.26.0-pre-rc1 + - except (because it uses go1.26 specific elements): + - `internal/testenv/testenv_unix.go` + - `internal/platform/zosarch.go` +- 2025-07-04: Sync with go1.24.4 +- 2025-04-14: Sync with go1.23.8 +- 2024-08-17: Sync with go1.22.6 +- 2023-02-28: Sync with go1.21.7 +- 2023-10-04: Sync with go1.20.8 +- 2023-10-04: Sync with go1.19.13 +- 2022-08-31: Sync with go1.18.5 + +## Notes + +### Packages + +- https://github.com/golang/go/blob/master/src/cmd/gofmt/ +- https://github.com/golang/go/blob/master/src/internal/cfg +- https://github.com/golang/go/blob/master/src/internal/goarch +- https://github.com/golang/go/blob/master/src/internal/testenv +- https://github.com/golang/go/blob/master/src/internal/platform +- https://github.com/golang/go/blob/master/src/internal/diff -> replaced by `github.com/rogpeppe/go-internal/diff` + +### Details + +`go/src/cmd/gofmt/internal.go` and `go/src/go/format/internal.go` are identical. +The `parserMode` is a global variable for `gofmt` and a constant for `go/format`. + +The constants (`tabWidth`, `printerMode`, `printerNormalizeNumbers`) are duplicated inside: +- [`go/src/cmd/gofmt/gofmt.go`](https://github.com/golang/go/blob/1b291b70dff51732415da5b68debe323704d8e8d/src/cmd/gofmt/gofmt.go#L49-L59) +- [`go/src/go/format/format.go`](https://github.com/golang/go/blob/1b291b70dff51732415da5b68debe323704d8e8d/src/go/format/format.go#L27-L37) + +Theoretically, only the following files are required: +- `gofmt.go` (only the constants (`tabWidth`, `printerMode`, `printerNormalizeNumbers`)) +- `internal.go` +- `LICENSE` +- `rewrite.go` +- `simplify.go` + +But it's easier to synchronize everything to follow changes. +But the isolation of `internal` packages from Go can be complex, so maybe, at some point, we will reduce the number of files and so remove the `internal/internal` directory (and the test files). diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/cache/cache.go b/vendor/github.com/golangci/golangci-lint/v2/internal/cache/cache.go index 138a3614..cf20b175 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/cache/cache.go +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/cache/cache.go @@ -290,7 +290,7 @@ func SetSalt(b *bytes.Buffer) { cache.SetSalt(b.Bytes()) } -func DefaultDir() string { - cacheDir, _ := cache.DefaultDir() - return cacheDir +func DefaultDir() (string, error) { + cacheDir, _, err := cache.DefaultDir() + return cacheDir, err } diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/go/base/readme.md b/vendor/github.com/golangci/golangci-lint/v2/internal/go/base/readme.md index 93afe9f2..49ba0c69 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/go/base/readme.md +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/go/base/readme.md @@ -6,5 +6,7 @@ Only the function `IsETXTBSY` is extracted. ## History +- https://github.com/golangci/golangci-lint/pull/6642 + - sync go1.26.4 (no change) - https://github.com/golangci/golangci-lint/pull/5576 - sync go1.24.1 diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/cache.go b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/cache.go index c514613d..1ac3f0fe 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/cache.go +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/cache.go @@ -303,6 +303,10 @@ func GetBytes(c Cache, id ActionID) ([]byte, Entry, error) { // GetMmap looks up the action ID in the cache and returns // the corresponding output bytes. // GetMmap should only be used for data that can be expected to fit in memory. +// The boolean result indicates whether the file was opened. +// If it is true, the caller should avoid attempting +// to write to the file on Windows, because Windows locks +// the open file, and writes to it will fail. func GetMmap(c Cache, id ActionID) ([]byte, Entry, bool, error) { entry, err := c.Get(id) if err != nil { @@ -388,13 +392,42 @@ func (c *DiskCache) Trim() error { // trim time is too far in the future, attempt the trim anyway. It's possible that // the cache was full when the corruption happened. Attempting a trim on // an empty cache is cheap, so there wouldn't be a big performance hit in that case. - if data, err := lockedfile.Read(filepath.Join(c.dir, "trim.txt")); err == nil { + skipTrim := func(data []byte) bool { if t, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64); err == nil { lastTrim := time.Unix(t, 0) if d := now.Sub(lastTrim); d < trimInterval && d > -mtimeInterval { - return nil + return true } } + return false + } + // Check to see if we need a trim. Do this check separately from the lockedfile.Transform + // so that we can skip getting an exclusive lock in the common case. + if data, err := lockedfile.Read(filepath.Join(c.dir, "trim.txt")); err == nil { + if skipTrim(data) { + return nil + } + } + + errFileChanged := errors.New("file changed") + + // Write the new timestamp before we start trimming to reduce the chance that multiple invocations + // try to trim at the same time, causing contention in CI (#76314). + err := lockedfile.Transform(filepath.Join(c.dir, "trim.txt"), func(data []byte) ([]byte, error) { + if skipTrim(data) { + // The timestamp in the file no longer meets the criteria for us to + // do a trim. It must have been updated by another go command invocation + // since we last read it. Skip the trim. + return nil, errFileChanged + } + return fmt.Appendf(nil, "%d", now.Unix()), nil + }) + if errors.Is(err, errors.ErrUnsupported) { + return err + } + if errors.Is(err, errFileChanged) { + // Skip the trim because we don't need it anymore. + return nil } // Trim each of the 256 subdirectories. @@ -406,14 +439,6 @@ func (c *DiskCache) Trim() error { c.trimSubdir(subdir, cutoff) } - // Ignore errors from here: if we don't write the complete timestamp, the - // cache will appear older than it is, and we'll trim it again next time. - var b bytes.Buffer - fmt.Fprintf(&b, "%d", now.Unix()) - if err := lockedfile.Write(filepath.Join(c.dir, "trim.txt"), &b, 0o666); err != nil { - return err - } - return nil } diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/default.go b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/default.go index cf38ab3d..a6cacaf1 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/default.go +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/default.go @@ -29,7 +29,10 @@ const cacheREADME = `This directory holds cached build artifacts from golangci-l // initDefaultCache does the work of finding the default cache // the first time Default is called. func initDefaultCache() Cache { - dir, _ := DefaultDir() + dir, _, err := DefaultDir() + if err != nil { + base.Fatalf("build cache is required, but could not be located: %v", err) + } if dir == "off" { if defaultDirErr != nil { base.Fatalf("build cache is required, but could not be located: %v", defaultDirErr) @@ -66,16 +69,27 @@ var ( // DefaultDir returns the effective GOLANGCI_LINT_CACHE setting. // It returns "off" if the cache is disabled, // and reports whether the effective value differs from GOLANGCI_LINT_CACHE. -func DefaultDir() (string, bool) { +func DefaultDir() (string, bool, error) { // Save the result of the first call to DefaultDir for later use in // initDefaultCache. cmd/go/main.go explicitly sets GOLANGCI_LINT_CACHE so that // subprocesses will inherit it, but that means initDefaultCache can't // otherwise distinguish between an explicit "off" and a UserCacheDir error. defaultDirOnce.Do(func() { - defaultDir = os.Getenv(envGolangciLintCache) - if defaultDir != "" { - defaultDirChanged = true + // Compute default location. + dir, err := os.UserCacheDir() + if err != nil { + defaultDir = "off" + defaultDirErr = fmt.Errorf("%s is not defined and %v", envGolangciLintCache, err) + } else { + defaultDir = filepath.Join(dir, "golangci-lint") + } + + newDir := os.Getenv(envGolangciLintCache) + if newDir != "" { + defaultDirErr = nil + defaultDirChanged = newDir != defaultDir + defaultDir = newDir if filepath.IsAbs(defaultDir) || defaultDir == "off" { return } @@ -83,17 +97,7 @@ func DefaultDir() (string, bool) { defaultDirErr = fmt.Errorf("%s is not an absolute path", envGolangciLintCache) return } - - // Compute default location. - dir, err := os.UserCacheDir() - if err != nil { - defaultDir = "off" - defaultDirChanged = true - defaultDirErr = fmt.Errorf("%s is not defined and %w", envGolangciLintCache, err) - return - } - defaultDir = filepath.Join(dir, "golangci-lint") }) - return defaultDir, defaultDirChanged + return defaultDir, defaultDirChanged, defaultDirErr } diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/hash.go b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/hash.go index 6a53dd88..3fa2422f 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/hash.go +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/hash.go @@ -43,6 +43,9 @@ func stripExperiment(version string) string { if i := strings.Index(version, " X:"); i >= 0 { return version[:i] } + if i := strings.Index(version, "-X:"); i >= 0 { + return version[:i] + } return version } diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/readme.md b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/readme.md index 66341fd4..6d33419f 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/readme.md +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cache/readme.md @@ -3,7 +3,7 @@ Extracted from `go/src/cmd/go/internal/cache/`. The main modifications are: -- The errors management +- The error management - Some methods return error. - Some errors are returned instead of being ignored. - The name of the env vars: @@ -12,6 +12,8 @@ The main modifications are: ## History +- https://github.com/golangci/golangci-lint/pull/6642 + - sync go1.26.4 - https://github.com/golangci/golangci-lint/pull/5576 - sync go1.24.1 - https://github.com/golangci/golangci-lint/pull/5100 @@ -30,7 +32,7 @@ The main modifications are: ## Previous History -Based on the initial PR/commit the based in a mix between go1.12 and go1.13: +Based on the initial PR/commit this was based in a mix between go1.12 and go1.13: - cache.go (go1.13) - cache_test.go (go1.12?) - default.go (go1.12?) diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cacheprog/cacheprog.go b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cacheprog/cacheprog.go index a2796592..9379636e 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cacheprog/cacheprog.go +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cacheprog/cacheprog.go @@ -76,9 +76,6 @@ type Request struct { ActionID []byte `json:",omitempty"` // or nil if not used // OutputID is stored with the body for "put" requests. - // - // Prior to Go 1.24, when GOCACHEPROG was still an experiment, this was - // accidentally named ObjectID. It was renamed to OutputID in Go 1.24. OutputID []byte `json:",omitempty"` // or nil if not used // Body is the body for "put" requests. It's sent after the JSON object @@ -91,14 +88,6 @@ type Request struct { // BodySize is the number of bytes of Body. If zero, the body isn't written. BodySize int64 `json:",omitempty"` - - // ObjectID is the accidental spelling of OutputID that was used prior to Go - // 1.24. - // - // Deprecated: use OutputID. This field is only populated temporarily for - // backwards compatibility with Go 1.23 and earlier when - // GOEXPERIMENT=gocacheprog is set. It will be removed in Go 1.25. - ObjectID []byte `json:",omitempty"` } // Response is the JSON response from the child process to the go command. @@ -125,7 +114,7 @@ type Response struct { // For "get" requests. Miss bool `json:",omitempty"` // cache miss - OutputID []byte `json:",omitempty"` // the ObjectID stored with the body + OutputID []byte `json:",omitempty"` // the OutputID stored with the body Size int64 `json:",omitempty"` // body size in bytes Time *time.Time `json:",omitempty"` // when the object was put in the cache (optional; used for cache expiration) diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cacheprog/readme.md b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cacheprog/readme.md index 1b08c848..defb1ada 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/go/cacheprog/readme.md +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/go/cacheprog/readme.md @@ -5,5 +5,7 @@ This is just a copy of the Go code without any changes. ## History +- https://github.com/golangci/golangci-lint/pull/6642 + - sync go1.26.4 - https://github.com/golangci/golangci-lint/pull/5576 - sync go1.24.1 diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/go/mmap/mmap.go b/vendor/github.com/golangci/golangci-lint/v2/internal/go/mmap/mmap.go index fd374df8..cd7ea80f 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/go/mmap/mmap.go +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/go/mmap/mmap.go @@ -22,11 +22,30 @@ type Data struct { } // Mmap maps the given file into memory. +// The boolean result indicates whether the file was opened. +// If it is true, the caller should avoid attempting +// to write to the file on Windows, because Windows locks +// the open file, and writes to it will fail. func Mmap(file string) (Data, bool, error) { f, err := os.Open(file) if err != nil { return Data{}, false, err } data, err := mmapFile(f) + + // Closing the file causes it not to count against this process's + // limit on open files; however, the mapping still counts against + // the system-wide limit, which is typically higher. Examples: + // + // macOS process (sysctl kern.maxfilesperproc): 61440 + // macOS system (sysctl kern.maxfiles): 122880 + // linux process (ulimit -n) 1048576 + // linux system (/proc/sys/fs/file-max) 100000 + if cerr := f.Close(); cerr != nil && err == nil { + return data, true, cerr + } + + // The file is still considered to be in use on Windows after + // it's closed because of the mapping. return data, true, err } diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/go/mmap/readme.md b/vendor/github.com/golangci/golangci-lint/v2/internal/go/mmap/readme.md index 5cbfdeef..764c2444 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/go/mmap/readme.md +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/go/mmap/readme.md @@ -5,6 +5,8 @@ This is just a copy of the Go code without any changes. ## History +- https://github.com/golangci/golangci-lint/pull/6642 + - sync go1.26.4 - https://github.com/golangci/golangci-lint/pull/5576 - sync go1.24.1 - https://github.com/golangci/golangci-lint/pull/5100 diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/go/quoted/readme.md b/vendor/github.com/golangci/golangci-lint/v2/internal/go/quoted/readme.md index 97868185..45467e92 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/go/quoted/readme.md +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/go/quoted/readme.md @@ -5,6 +5,8 @@ This is just a copy of the Go code without any changes. ## History +- https://github.com/golangci/golangci-lint/pull/6642 + - sync go1.26.4 (no change) - https://github.com/golangci/golangci-lint/pull/5576 - sync go1.24.1 (no change) - https://github.com/golangci/golangci-lint/pull/5100 diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/lcs/old.go b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/lcs/old.go index 4c346706..d6265c8c 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/lcs/old.go +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/lcs/old.go @@ -16,10 +16,6 @@ type Diff struct { ReplStart, ReplEnd int // offset of replacement text in B } -// DiffStrings returns the differences between two strings. -// It does not respect rune boundaries. -func DiffStrings(a, b string) []Diff { return diff(stringSeqs{a, b}) } - // DiffBytes returns the differences between two byte sequences. // It does not respect rune boundaries. func DiffBytes(a, b []byte) []Diff { return diff(bytesSeqs{a, b}) } @@ -27,9 +23,13 @@ func DiffBytes(a, b []byte) []Diff { return diff(bytesSeqs{a, b}) } // DiffRunes returns the differences between two rune sequences. func DiffRunes(a, b []rune) []Diff { return diff(runesSeqs{a, b}) } +// DiffLines returns the differences between two string sequences. +func DiffLines(a, b []string) []Diff { return diff(linesSeqs{a, b}) } + +// A limit on how deeply the LCS algorithm should search. The value is just a guess. +var maxDiffs = 100 + func diff(seqs sequences) []Diff { - // A limit on how deeply the LCS algorithm should search. The value is just a guess. - const maxDiffs = 100 diff, _ := compute(seqs, twosided, maxDiffs/2) return diff } @@ -378,10 +378,7 @@ func (e *editGraph) twoDone(df, db int) (int, bool) { return 0, false // diagonals cannot overlap } kmin := max(-df, -db+e.delta) - kmax := db + e.delta - if df < kmax { - kmax = df - } + kmax := min(df, db+e.delta) for k := kmin; k <= kmax; k += 2 { x := e.vf.get(df, k) u := e.vb.get(db, k-e.delta) diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/lcs/sequence.go b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/lcs/sequence.go index 2d72d263..429e8c61 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/lcs/sequence.go +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/lcs/sequence.go @@ -13,63 +13,44 @@ type sequences interface { commonSuffixLen(ai, aj, bi, bj int) int // len(commonSuffix(A[ai:aj], B[bi:bj])) } -type stringSeqs struct{ a, b string } - -func (s stringSeqs) lengths() (int, int) { return len(s.a), len(s.b) } -func (s stringSeqs) commonPrefixLen(ai, aj, bi, bj int) int { - return commonPrefixLenString(s.a[ai:aj], s.b[bi:bj]) -} -func (s stringSeqs) commonSuffixLen(ai, aj, bi, bj int) int { - return commonSuffixLenString(s.a[ai:aj], s.b[bi:bj]) -} - // The explicit capacity in s[i:j:j] leads to more efficient code. type bytesSeqs struct{ a, b []byte } func (s bytesSeqs) lengths() (int, int) { return len(s.a), len(s.b) } func (s bytesSeqs) commonPrefixLen(ai, aj, bi, bj int) int { - return commonPrefixLenBytes(s.a[ai:aj:aj], s.b[bi:bj:bj]) + return commonPrefixLen(s.a[ai:aj:aj], s.b[bi:bj:bj]) } func (s bytesSeqs) commonSuffixLen(ai, aj, bi, bj int) int { - return commonSuffixLenBytes(s.a[ai:aj:aj], s.b[bi:bj:bj]) + return commonSuffixLen(s.a[ai:aj:aj], s.b[bi:bj:bj]) } type runesSeqs struct{ a, b []rune } func (s runesSeqs) lengths() (int, int) { return len(s.a), len(s.b) } func (s runesSeqs) commonPrefixLen(ai, aj, bi, bj int) int { - return commonPrefixLenRunes(s.a[ai:aj:aj], s.b[bi:bj:bj]) + return commonPrefixLen(s.a[ai:aj:aj], s.b[bi:bj:bj]) } func (s runesSeqs) commonSuffixLen(ai, aj, bi, bj int) int { - return commonSuffixLenRunes(s.a[ai:aj:aj], s.b[bi:bj:bj]) + return commonSuffixLen(s.a[ai:aj:aj], s.b[bi:bj:bj]) +} + +type linesSeqs struct{ a, b []string } + +func (s linesSeqs) lengths() (int, int) { return len(s.a), len(s.b) } +func (s linesSeqs) commonPrefixLen(ai, aj, bi, bj int) int { + return commonPrefixLen(s.a[ai:aj], s.b[bi:bj]) +} +func (s linesSeqs) commonSuffixLen(ai, aj, bi, bj int) int { + return commonSuffixLen(s.a[ai:aj], s.b[bi:bj]) } // TODO(adonovan): optimize these functions using ideas from: // - https://go.dev/cl/408116 common.go // - https://go.dev/cl/421435 xor_generic.go -// TODO(adonovan): factor using generics when available, -// but measure performance impact. - -// commonPrefixLen* returns the length of the common prefix of a[ai:aj] and b[bi:bj]. -func commonPrefixLenBytes(a, b []byte) int { - n := min(len(a), len(b)) - i := 0 - for i < n && a[i] == b[i] { - i++ - } - return i -} -func commonPrefixLenRunes(a, b []rune) int { - n := min(len(a), len(b)) - i := 0 - for i < n && a[i] == b[i] { - i++ - } - return i -} -func commonPrefixLenString(a, b string) int { +// commonPrefixLen returns the length of the common prefix of a[ai:aj] and b[bi:bj]. +func commonPrefixLen[T comparable](a, b []T) int { n := min(len(a), len(b)) i := 0 for i < n && a[i] == b[i] { @@ -78,16 +59,8 @@ func commonPrefixLenString(a, b string) int { return i } -// commonSuffixLen* returns the length of the common suffix of a[ai:aj] and b[bi:bj]. -func commonSuffixLenBytes(a, b []byte) int { - n := min(len(a), len(b)) - i := 0 - for i < n && a[len(a)-1-i] == b[len(b)-1-i] { - i++ - } - return i -} -func commonSuffixLenRunes(a, b []rune) int { +// commonSuffixLen returns the length of the common suffix of a[ai:aj] and b[bi:bj]. +func commonSuffixLen[T comparable](a, b []T) int { n := min(len(a), len(b)) i := 0 for i < n && a[len(a)-1-i] == b[len(b)-1-i] { @@ -95,19 +68,3 @@ func commonSuffixLenRunes(a, b []rune) int { } return i } -func commonSuffixLenString(a, b string) int { - n := min(len(a), len(b)) - i := 0 - for i < n && a[len(a)-1-i] == b[len(b)-1-i] { - i++ - } - return i -} - -func min(x, y int) int { - if x < y { - return x - } else { - return y - } -} diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/ndiff.go b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/ndiff.go index 1c64d1ec..5083a7ea 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/ndiff.go +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/ndiff.go @@ -6,11 +6,30 @@ package diff import ( "bytes" + "strings" "unicode/utf8" "github.com/golangci/golangci-lint/v2/internal/x/tools/diff/lcs" ) +// Lines computes differences between two strings. All edits are at line boundaries. +func Lines(before, after string) []Edit { + beforeLines, bOffsets := splitLines(before) + afterLines, _ := splitLines(after) + diffs := lcs.DiffLines(beforeLines, afterLines) + + // Convert from LCS diffs to Edits + res := make([]Edit, len(diffs)) + for i, d := range diffs { + res[i] = Edit{ + Start: bOffsets[d.Start], + End: bOffsets[d.End], + New: strings.Join(afterLines[d.ReplStart:d.ReplEnd], ""), + } + } + return res +} + // Strings computes the differences between two strings. // The resulting edits respect rune boundaries. func Strings(before, after string) []Edit { diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/readme.md b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/readme.md index b28e41d9..7262b45a 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/readme.md +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/readme.md @@ -5,6 +5,8 @@ This is just a copy of the code without any changes. ## History +- https://github.com/golangci/golangci-lint/pull/6642 + - sync with https://github.com/golang/tools/blob/v0.48.0/internal/diff/ - https://github.com/golangci/golangci-lint/pull/6076 - sync with https://github.com/golang/tools/blob/v0.37.0/internal/diff/ - https://github.com/golangci/golangci-lint/pull/5576 diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/unified.go b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/unified.go index 9a786dbb..8fc011e4 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/unified.go +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/diff/unified.go @@ -7,6 +7,8 @@ package diff import ( "fmt" "log" + "regexp" + "strconv" "strings" ) @@ -18,7 +20,7 @@ const DefaultContextLines = 3 // The old and new labels are the names of the old and new files. // If the strings are equal, it returns the empty string. func Unified(oldLabel, newLabel, old, new string) string { - edits := Strings(old, new) + edits := Lines(old, new) unified, err := ToUnified(oldLabel, newLabel, old, edits, DefaultContextLines) if err != nil { // Can't happen: edits are consistent. @@ -114,7 +116,7 @@ func toUnified(fromName, toName string, content string, edits []Edit, contextLin if err != nil { return u, err } - lines := splitLines(content) + lines, _ := splitLines(content) var h *hunk last := 0 toLine := 0 @@ -156,7 +158,8 @@ func toUnified(fromName, toName string, content string, edits []Edit, contextLin last++ } if edit.New != "" { - for _, content := range splitLines(edit.New) { + v, _ := splitLines(edit.New) + for _, content := range v { h.lines = append(h.lines, line{kind: opInsert, content: content}) toLine++ } @@ -170,12 +173,24 @@ func toUnified(fromName, toName string, content string, edits []Edit, contextLin return u, nil } -func splitLines(text string) []string { - lines := strings.SplitAfter(text, "\n") - if lines[len(lines)-1] == "" { - lines = lines[:len(lines)-1] +// split into lines removing a final empty line, +// and also return the offsets of the line beginnings. +func splitLines(text string) ([]string, []int) { + var lines []string + offsets := []int{0} + start := 0 + for i, r := range text { + if r == '\n' { + lines = append(lines, text[start:i+1]) + start = i + 1 + offsets = append(offsets, start) + } + } + if start < len(text) { + lines = append(lines, text[start:]) + offsets = append(offsets, len(text)) } - return lines + return lines, offsets } func addEqualLines(h *hunk, lines []string, start, end int) int { @@ -249,3 +264,51 @@ func (u unified) String() string { } return b.String() } + +// ApplyUnified applies the unified diffs. +func ApplyUnified(udiffs, bef string) (string, error) { + before := strings.Split(bef, "\n") + unif := strings.Split(udiffs, "\n") + var got []string + left := 0 + // parse and apply the unified diffs + for _, l := range unif { + if len(l) == 0 { + continue // probably the last line (from Split) + } + switch l[0] { + case '@': // The @@ line + m := atregexp.FindStringSubmatch(l) + fromLine, err := strconv.Atoi(m[1]) + if err != nil { + return "", fmt.Errorf("missing line number in %q", l) + } + // before is a slice, so0-based; fromLine is 1-based + for ; left < fromLine-1; left++ { + got = append(got, before[left]) + } + case '+': // add this line + if strings.HasPrefix(l, "+++ ") { + continue + } + got = append(got, l[1:]) + case '-': // delete this line + if strings.HasPrefix(l, "--- ") { + continue + } + left++ + case ' ': + return "", fmt.Errorf("unexpected line %q", l) + default: + return "", fmt.Errorf("invalid unified diff: <<%s>>", udiffs) + } + } + // copy any remaining lines + for ; left < len(before); left++ { + got = append(got, before[left]) + } + return strings.Join(got, "\n"), nil +} + +// The first number in the @@ lines is the line number in the 'before' data +var atregexp = regexp.MustCompile(`@@ -(\d+).* @@`) diff --git a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/driverutil/readme.md b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/driverutil/readme.md index 8720fb6f..1ee6259d 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/driverutil/readme.md +++ b/vendor/github.com/golangci/golangci-lint/v2/internal/x/tools/driverutil/readme.md @@ -7,6 +7,9 @@ Previously, it was `analysisinternal` and `analysisflags` packages. ## History +- https://github.com/golangci/golangci-lint/pull/6642 (no changes) + - sync with https://github.com/golang/tools/blob/v0.48.0/internal/analysis/driverutil/readfile.go + - https://github.com/golangci/golangci-lint/pull/6434 - sync with https://github.com/golang/tools/blob/v0.43.0/internal/analysis/driverutil/readfile.go diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/custom-gcl.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/custom-gcl.jsonschema.json new file mode 100644 index 00000000..71ea3e98 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/custom-gcl.jsonschema.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$ref": "#/$defs/Configuration", + "$defs": { + "Configuration": { + "properties": { + "version": { + "type": "string", + "description": "golangci-lint version." + }, + "name": { + "type": "string", + "description": "Name of the binary." + }, + "destination": { + "type": "string", + "description": "Destination is the path to a directory to store the binary." + }, + "plugins": { + "items": { + "$ref": "#/$defs/Plugin" + }, + "type": "array", + "description": "Plugins information." + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "version" + ], + "description": "Configuration represents the configuration file." + }, + "Plugin": { + "oneOf": [ + { + "required": [ + "version" + ], + "title": "version" + }, + { + "required": [ + "path" + ], + "title": "path" + } + ], + "properties": { + "module": { + "type": "string", + "description": "Module name." + }, + "import": { + "type": "string", + "description": "Import to use." + }, + "version": { + "type": "string", + "description": "Version of the module.\nOnly for module available through a Go proxy." + }, + "path": { + "type": "string", + "description": "Path to the local module.\nOnly for local module." + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "module" + ], + "description": "Plugin represents information about a plugin." + } + }, + "description": "mygcl configuration definition file" +} \ No newline at end of file diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.jsonschema.json new file mode 100644 index 00000000..bb4f0398 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.jsonschema.json @@ -0,0 +1,5575 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupOption", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr", + "zeroByteRepeat" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "godoclint-rules": { + "enum": [ + "pkg-doc", + "single-pkg-doc", + "require-pkg-doc", + "start-with-name", + "require-doc", + "deprecated", + "max-len", + "no-unused-link", + "require-stdlib-doclink" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G116", + "G117", + "G118", + "G119", + "G120", + "G121", + "G122", + "G123", + "G124", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G408", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602", + "G701", + "G702", + "G703", + "G704", + "G705", + "G706", + "G707", + "G708", + "G709", + "G710" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "inline", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "epoch-naming", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "forbidden-call-in-wg-go", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "identical-ifelseif-branches", + "identical-ifelseif-conditions", + "identical-switch-branches", + "identical-switch-conditions", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "inefficient-map-lookup", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "package-naming", + "package-directory-mismatch", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-if", + "unnecessary-stmt", + "unreachable-code", + "unsecure-url-scheme", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "use-slices-sort", + "use-waitgroup-go", + "useless-break", + "useless-fallthrough", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported", + "unusedmethod" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "modernize-analyzers": { + "enum": [ + "any", + "atomictypes", + "embedlit", + "errorsastype", + "forvar", + "importcomment", + "mapsloop", + "minmax", + "newexpr", + "omitzero", + "plusbuild", + "rangeint", + "reflecttypeassert", + "reflecttypefor", + "slicesbackward", + "slicesclip", + "slicescontains", + "slicessort", + "stditerators", + "stringscut", + "stringscutprefix", + "stringsseq", + "stringsbuilder", + "testingcontext", + "unsafefuncs", + "waitgroupgo" + ] + }, + "wsl-checks": { + "enum": [ + "after-block", + "after-decl", + "after-defer", + "after-expr", + "after-go", + "append", + "assign-exclusive", + "assign-expr", + "assign", + "branch", + "cuddle-group", + "decl", + "defer", + "err", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "leading-whitespace", + "range", + "return", + "select", + "send", + "switch", + "trailing-whitespace", + "type-switch" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "clickhouselint", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exhaustruct_v5", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godoclint", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "gomodguard_v2", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "iotamixing", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "modernize", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + }, + "comments-only": { + "description": "Checks only comments, skip strings.", + "type": "boolean", + "default": false + }, + "skip-raw-strings": { + "description": "Skip raw string literals (backtick-delimited) from duplicate word checking.", + "type": "boolean", + "default": false + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "bodycloseSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-consumption": { + "description": "Check that the response body is consumed.", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "empty-line": { + "description": "Checks that there is an empty space between the embedded fields and regular fields.", + "type": "boolean", + "default": false + }, + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructv5Settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enforce-patterns": { + "description": "List of regular expressions to match type names that should be checked.", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-patterns":{ + "description": "List of regular expressions to match type names that should be skipped from checking.", + "type": "array", + "items": { + "type": "string" + } + }, + "optional-patterns": { + "description": "List of regular expressions to match type names where all fields are treated as optional.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty":{ + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-patterns": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + }, + "explicit-mode":{ + "description": "When true, only types marked with //exhaustruct:enforce directive or matching enforce-rx patterns are checked.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + }, + "check-loops": { + "description": "Disable detection of fat contexts in function literals.", + "type": "boolean", + "default": true + }, + "check-function-literals": { + "description": "Disable detection of fat contexts in function literals.", + "type": "boolean", + "default": true + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + }, + "function": { + "description": "Checks that exported functions are placed before unexported functions.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + }, + "force-tonot": { + "description": "Force using `ToNot`, `ShouldNot` instead of `To(Not())`.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "exclude-types": { + "type": "array", + "items": { + "enum": [ + "Assignment", + "Binary", + "Case", + "Return", + "Call", + "CompositeLit" + ] + } + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + }, + "ignore-tests": { + "description": "Ignore strings from test files", + "type": "boolean", + "default": false + }, + "ignore-functions": { + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-map-keys": { + "description": "Ignore string literals used as map keys", + "type": "boolean" + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godoclintSettings": { + "type": "object", + "properties": { + "default": { + "type": "string", + "enum": ["all", "basic", "none"], + "default": "basic", + "description": "Default set of rules to enable." + }, + "enable": { + "description": "List of rules to enable in addition to the default set.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "disable": { + "description": "List of rules to disable.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "options": { + "type": "object", + "description": "A map for setting individual rule options.", + "properties": { + "max-len": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "description": "Maximum line length for godocs, not including the `//`, `/*` or `*/` tokens.", + "default": 77 + } + } + }, + "require-doc": { + "type": "object", + "properties": { + "ignore-exported": { + "type": "boolean", + "description": "Ignore exported (public) symbols when applying the `require-doc` rule.", + "default": false + }, + "ignore-unexported": { + "type": "boolean", + "description": "Ignore unexported (private) symbols when applying the `require-doc` rule.", + "default": true + } + } + }, + "start-with-name": { + "type": "object", + "properties": { + "include-unexported": { + "type": "boolean", + "description": "Include unexported symbols when applying the `start-with-name` rule.", + "default": false + } + } + } + } + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + }, + "extra": { + "type": "object", + "additionalProperties": false, + "properties": { + "group-params": { + "type": "boolean", + "default": false + }, + "clothe-returns": { + "type": "boolean", + "default": false + }, + "balance-calls": { + "type": "boolean", + "default": false + } + } + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "replace-allow-all": { + "description": "Allow all `replace` directives.", + "type": "boolean", + "default": false + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + }, + "check-module-path": { + "description": "Check the validity of the module path.", + "type": "boolean", + "default": false + } + } + }, + "gomodguardv2Settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-replace-directives": { + "type": "boolean" + }, + "allowed": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["module"], + "properties": { + "module": { + "type": "string" + }, + "version": { + "type": "string" + }, + "match-type": { + "enum": ["", "exact", "prefix", "regex"], + "default": "exact" + } + } + } + }, + "blocked": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["module"], + "properties": { + "module": { + "type": "string" + }, + "version": { + "type": "string" + }, + "match-type": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "recommendations": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "unusedmethod": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ineffassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-escaping-errors": { + "description": "Check escaping variables of type error, may cause false positives.", + "type": "boolean", + "default": false + } + } + }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "modernizeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "description": "List of analyzers to disable.", + "type": "array", + "items": { + "$ref": "#/definitions/modernize-analyzers" + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + }, + "allow-unused-named-returns": { + "description": "Allow named returns in the signature but report them if referenced in the body or used by a naked return.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + }, + "check-cleanup": { + "description": "Check that defer is not used with t.Parallel (use t.Cleanup instead).", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + }, + "concat-loop": { + "description": "Enable/disable optimization of concat loop.", + "type": "boolean", + "default": true + }, + "loop-other-ops": { + "description": "Optimization of `concat-loop` even with other operations.", + "type": "boolean", + "default": false + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "enable-default-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-global": { + "description": "Report the use of global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "context": { + "description": "Report the use of functions without a context.Context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Report dynamic log messages, such as those that are built with fmt.Sprintf.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Report log messages that do not match a particular style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "no-mixed-args": { + "description": "Report the use of both key-value pairs and attributes within a single function call.", + "type": "boolean", + "default": true + }, + "kv-only": { + "description": "Report any use of attributes as function call arguments.", + "type": "boolean", + "default": false + }, + "attr-only": { + "description": "Report any use of key-value pairs as function call arguments.", + "type": "boolean", + "default": false + }, + "args-on-sep-lines": { + "description": "Report two or more arguments on the same line.", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Report the use of string literals as log keys.", + "type": "boolean", + "default": false + }, + "allowed-keys": { + "description": "Report the use of log keys that are not explicitly allowed.", + "type": "array", + "items": { + "type": "string" + } + }, + "forbidden-keys": { + "description": "Report the use of forbidden log keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "key-naming-case": { + "description": "Report log keys that do not match a particular naming case.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "custom-funcs": { + "description": "Analyze custom functions in addition to the standard log/slog functions.", + "type": "array", + "items": { + "$ref": "#/definitions/settings/definitions/sloglintCustomFunc" + } + } + } + }, + "sloglintCustomFunc": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "The full name of the function, including the package. If the function is a method, the receiver type must be wrapped in parentheses.", + "type": "string" + }, + "msg-pos": { + "description": "The position of the \"msg string\" argument in the function signature, starting from 0. If there is no message in the function, a negative value must be passed.", + "type": "integer" + }, + "args-pos": { + "description": "The position of the \"args ...any\" argument in the function signature, starting from 0. If there are no arguments in the function, a negative value must be passed.", + "type": "integer" + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unqueryvetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-sql-builders": { + "description": "Enable SQL builder checking.", + "type": "boolean", + "default": true + }, + "check-aliased-wildcard": { + "description": "Enable aliased wildcard detection like SELECT t.*.", + "type": "boolean", + "default": true + }, + "check-string-concat": { + "description": "Enable string concatenation analysis.", + "type": "boolean", + "default": true + }, + "check-format-strings": { + "description": "Enable format string analysis like fmt.Sprintf.", + "type": "boolean", + "default": true + }, + "check-string-builder": { + "description": "Enable strings.Builder analysis.", + "type": "boolean", + "default": true + }, + "check-subqueries": { + "description": "Enable subquery analysis.", + "type": "boolean", + "default": true + }, + "check-n1": { + "type": "boolean", + "default": false + }, + "check-sql-injection": { + "type": "boolean", + "default": false + }, + "check-tx-leaks": { + "type": "boolean", + "default": false + }, + "allowed-patterns": { + "description": "Regex patterns for acceptable SELECT * usage.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "Allow is a list of SQL patterns to allow (whitelist).", + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Functions to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "sql-builders": { + "type": "object", + "additionalProperties": false, + "properties": { + "squirrel": { + "type": "boolean", + "default": true + }, + "gorm": { + "type": "boolean", + "default": true + }, + "sqlx": { + "type": "boolean", + "default": true + }, + "ent": { + "type": "boolean", + "default": true + }, + "pgx": { + "type": "boolean", + "default": true + }, + "bun": { + "type": "boolean", + "default": true + }, + "sqlboiler": { + "type": "boolean", + "default": true + }, + "jet": { + "type": "boolean", + "default": true + } + } + }, + "custom-rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "pattern": { + "type": "string" + }, + "patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "when": { + "type": "string" + }, + "message": { + "type": "string" + }, + "action": { + "type": "string" + } + } + } + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "cuddle-max-statements": { + "type": "integer", + "default": 1 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "enable-build-vcs": { + "type": "boolean", + "default": false + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "bodyclose": { + "$ref": "#/definitions/settings/definitions/bodycloseSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "exhaustruct_v5": { + "$ref": "#/definitions/settings/definitions/exhaustructv5Settings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godoclint": { + "$ref": "#/definitions/settings/definitions/godoclintSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gomodguard_v2": { + "$ref": "#/definitions/settings/definitions/gomodguardv2Settings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ineffassign": { + "$ref": "#/definitions/settings/definitions/ineffassignSettings" + }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "modernize": { + "$ref": "#/definitions/settings/definitions/modernizeSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unqueryvet": { + "$ref": "#/definitions/settings/definitions/unqueryvetSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Apply the fixes detected by the linters and formatters (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.next.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.next.jsonschema.json new file mode 100644 index 00000000..bb4f0398 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.next.jsonschema.json @@ -0,0 +1,5575 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupOption", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr", + "zeroByteRepeat" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "godoclint-rules": { + "enum": [ + "pkg-doc", + "single-pkg-doc", + "require-pkg-doc", + "start-with-name", + "require-doc", + "deprecated", + "max-len", + "no-unused-link", + "require-stdlib-doclink" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G116", + "G117", + "G118", + "G119", + "G120", + "G121", + "G122", + "G123", + "G124", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G408", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602", + "G701", + "G702", + "G703", + "G704", + "G705", + "G706", + "G707", + "G708", + "G709", + "G710" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "inline", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "epoch-naming", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "forbidden-call-in-wg-go", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "identical-ifelseif-branches", + "identical-ifelseif-conditions", + "identical-switch-branches", + "identical-switch-conditions", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "inefficient-map-lookup", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "package-naming", + "package-directory-mismatch", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-if", + "unnecessary-stmt", + "unreachable-code", + "unsecure-url-scheme", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "use-slices-sort", + "use-waitgroup-go", + "useless-break", + "useless-fallthrough", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported", + "unusedmethod" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "modernize-analyzers": { + "enum": [ + "any", + "atomictypes", + "embedlit", + "errorsastype", + "forvar", + "importcomment", + "mapsloop", + "minmax", + "newexpr", + "omitzero", + "plusbuild", + "rangeint", + "reflecttypeassert", + "reflecttypefor", + "slicesbackward", + "slicesclip", + "slicescontains", + "slicessort", + "stditerators", + "stringscut", + "stringscutprefix", + "stringsseq", + "stringsbuilder", + "testingcontext", + "unsafefuncs", + "waitgroupgo" + ] + }, + "wsl-checks": { + "enum": [ + "after-block", + "after-decl", + "after-defer", + "after-expr", + "after-go", + "append", + "assign-exclusive", + "assign-expr", + "assign", + "branch", + "cuddle-group", + "decl", + "defer", + "err", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "leading-whitespace", + "range", + "return", + "select", + "send", + "switch", + "trailing-whitespace", + "type-switch" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "clickhouselint", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exhaustruct_v5", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godoclint", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "gomodguard_v2", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "iotamixing", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "modernize", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + }, + "comments-only": { + "description": "Checks only comments, skip strings.", + "type": "boolean", + "default": false + }, + "skip-raw-strings": { + "description": "Skip raw string literals (backtick-delimited) from duplicate word checking.", + "type": "boolean", + "default": false + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "bodycloseSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-consumption": { + "description": "Check that the response body is consumed.", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "empty-line": { + "description": "Checks that there is an empty space between the embedded fields and regular fields.", + "type": "boolean", + "default": false + }, + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructv5Settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enforce-patterns": { + "description": "List of regular expressions to match type names that should be checked.", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-patterns":{ + "description": "List of regular expressions to match type names that should be skipped from checking.", + "type": "array", + "items": { + "type": "string" + } + }, + "optional-patterns": { + "description": "List of regular expressions to match type names where all fields are treated as optional.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty":{ + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-patterns": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + }, + "explicit-mode":{ + "description": "When true, only types marked with //exhaustruct:enforce directive or matching enforce-rx patterns are checked.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + }, + "check-loops": { + "description": "Disable detection of fat contexts in function literals.", + "type": "boolean", + "default": true + }, + "check-function-literals": { + "description": "Disable detection of fat contexts in function literals.", + "type": "boolean", + "default": true + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + }, + "function": { + "description": "Checks that exported functions are placed before unexported functions.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + }, + "force-tonot": { + "description": "Force using `ToNot`, `ShouldNot` instead of `To(Not())`.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "exclude-types": { + "type": "array", + "items": { + "enum": [ + "Assignment", + "Binary", + "Case", + "Return", + "Call", + "CompositeLit" + ] + } + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + }, + "ignore-tests": { + "description": "Ignore strings from test files", + "type": "boolean", + "default": false + }, + "ignore-functions": { + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-map-keys": { + "description": "Ignore string literals used as map keys", + "type": "boolean" + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godoclintSettings": { + "type": "object", + "properties": { + "default": { + "type": "string", + "enum": ["all", "basic", "none"], + "default": "basic", + "description": "Default set of rules to enable." + }, + "enable": { + "description": "List of rules to enable in addition to the default set.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "disable": { + "description": "List of rules to disable.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "options": { + "type": "object", + "description": "A map for setting individual rule options.", + "properties": { + "max-len": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "description": "Maximum line length for godocs, not including the `//`, `/*` or `*/` tokens.", + "default": 77 + } + } + }, + "require-doc": { + "type": "object", + "properties": { + "ignore-exported": { + "type": "boolean", + "description": "Ignore exported (public) symbols when applying the `require-doc` rule.", + "default": false + }, + "ignore-unexported": { + "type": "boolean", + "description": "Ignore unexported (private) symbols when applying the `require-doc` rule.", + "default": true + } + } + }, + "start-with-name": { + "type": "object", + "properties": { + "include-unexported": { + "type": "boolean", + "description": "Include unexported symbols when applying the `start-with-name` rule.", + "default": false + } + } + } + } + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + }, + "extra": { + "type": "object", + "additionalProperties": false, + "properties": { + "group-params": { + "type": "boolean", + "default": false + }, + "clothe-returns": { + "type": "boolean", + "default": false + }, + "balance-calls": { + "type": "boolean", + "default": false + } + } + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "replace-allow-all": { + "description": "Allow all `replace` directives.", + "type": "boolean", + "default": false + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + }, + "check-module-path": { + "description": "Check the validity of the module path.", + "type": "boolean", + "default": false + } + } + }, + "gomodguardv2Settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-replace-directives": { + "type": "boolean" + }, + "allowed": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["module"], + "properties": { + "module": { + "type": "string" + }, + "version": { + "type": "string" + }, + "match-type": { + "enum": ["", "exact", "prefix", "regex"], + "default": "exact" + } + } + } + }, + "blocked": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["module"], + "properties": { + "module": { + "type": "string" + }, + "version": { + "type": "string" + }, + "match-type": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "recommendations": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "unusedmethod": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ineffassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-escaping-errors": { + "description": "Check escaping variables of type error, may cause false positives.", + "type": "boolean", + "default": false + } + } + }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "modernizeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "description": "List of analyzers to disable.", + "type": "array", + "items": { + "$ref": "#/definitions/modernize-analyzers" + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + }, + "allow-unused-named-returns": { + "description": "Allow named returns in the signature but report them if referenced in the body or used by a naked return.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + }, + "check-cleanup": { + "description": "Check that defer is not used with t.Parallel (use t.Cleanup instead).", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + }, + "concat-loop": { + "description": "Enable/disable optimization of concat loop.", + "type": "boolean", + "default": true + }, + "loop-other-ops": { + "description": "Optimization of `concat-loop` even with other operations.", + "type": "boolean", + "default": false + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "enable-default-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-global": { + "description": "Report the use of global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "context": { + "description": "Report the use of functions without a context.Context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Report dynamic log messages, such as those that are built with fmt.Sprintf.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Report log messages that do not match a particular style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "no-mixed-args": { + "description": "Report the use of both key-value pairs and attributes within a single function call.", + "type": "boolean", + "default": true + }, + "kv-only": { + "description": "Report any use of attributes as function call arguments.", + "type": "boolean", + "default": false + }, + "attr-only": { + "description": "Report any use of key-value pairs as function call arguments.", + "type": "boolean", + "default": false + }, + "args-on-sep-lines": { + "description": "Report two or more arguments on the same line.", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Report the use of string literals as log keys.", + "type": "boolean", + "default": false + }, + "allowed-keys": { + "description": "Report the use of log keys that are not explicitly allowed.", + "type": "array", + "items": { + "type": "string" + } + }, + "forbidden-keys": { + "description": "Report the use of forbidden log keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "key-naming-case": { + "description": "Report log keys that do not match a particular naming case.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "custom-funcs": { + "description": "Analyze custom functions in addition to the standard log/slog functions.", + "type": "array", + "items": { + "$ref": "#/definitions/settings/definitions/sloglintCustomFunc" + } + } + } + }, + "sloglintCustomFunc": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "The full name of the function, including the package. If the function is a method, the receiver type must be wrapped in parentheses.", + "type": "string" + }, + "msg-pos": { + "description": "The position of the \"msg string\" argument in the function signature, starting from 0. If there is no message in the function, a negative value must be passed.", + "type": "integer" + }, + "args-pos": { + "description": "The position of the \"args ...any\" argument in the function signature, starting from 0. If there are no arguments in the function, a negative value must be passed.", + "type": "integer" + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unqueryvetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-sql-builders": { + "description": "Enable SQL builder checking.", + "type": "boolean", + "default": true + }, + "check-aliased-wildcard": { + "description": "Enable aliased wildcard detection like SELECT t.*.", + "type": "boolean", + "default": true + }, + "check-string-concat": { + "description": "Enable string concatenation analysis.", + "type": "boolean", + "default": true + }, + "check-format-strings": { + "description": "Enable format string analysis like fmt.Sprintf.", + "type": "boolean", + "default": true + }, + "check-string-builder": { + "description": "Enable strings.Builder analysis.", + "type": "boolean", + "default": true + }, + "check-subqueries": { + "description": "Enable subquery analysis.", + "type": "boolean", + "default": true + }, + "check-n1": { + "type": "boolean", + "default": false + }, + "check-sql-injection": { + "type": "boolean", + "default": false + }, + "check-tx-leaks": { + "type": "boolean", + "default": false + }, + "allowed-patterns": { + "description": "Regex patterns for acceptable SELECT * usage.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "Allow is a list of SQL patterns to allow (whitelist).", + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Functions to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "sql-builders": { + "type": "object", + "additionalProperties": false, + "properties": { + "squirrel": { + "type": "boolean", + "default": true + }, + "gorm": { + "type": "boolean", + "default": true + }, + "sqlx": { + "type": "boolean", + "default": true + }, + "ent": { + "type": "boolean", + "default": true + }, + "pgx": { + "type": "boolean", + "default": true + }, + "bun": { + "type": "boolean", + "default": true + }, + "sqlboiler": { + "type": "boolean", + "default": true + }, + "jet": { + "type": "boolean", + "default": true + } + } + }, + "custom-rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "pattern": { + "type": "string" + }, + "patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "when": { + "type": "string" + }, + "message": { + "type": "string" + }, + "action": { + "type": "string" + } + } + } + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "cuddle-max-statements": { + "type": "integer", + "default": 1 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "enable-build-vcs": { + "type": "boolean", + "default": false + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "bodyclose": { + "$ref": "#/definitions/settings/definitions/bodycloseSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "exhaustruct_v5": { + "$ref": "#/definitions/settings/definitions/exhaustructv5Settings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godoclint": { + "$ref": "#/definitions/settings/definitions/godoclintSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gomodguard_v2": { + "$ref": "#/definitions/settings/definitions/gomodguardv2Settings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ineffassign": { + "$ref": "#/definitions/settings/definitions/ineffassignSettings" + }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "modernize": { + "$ref": "#/definitions/settings/definitions/modernizeSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unqueryvet": { + "$ref": "#/definitions/settings/definitions/unqueryvetSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Apply the fixes detected by the linters and formatters (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.57.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.57.jsonschema.json new file mode 100644 index 00000000..a97c0d42 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.57.jsonschema.json @@ -0,0 +1,3480 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentedOutCode", + "commentedOutImport", + "commentFormatting", + "defaultCaseOrder", + "deferUnlambda", + "deferInLoop", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringsCompare", + "stringXbytes", + "suspiciousSorting", + "switchTrue", + "syncMapLoadAndDelete", + "timeCmpSimplify", + "timeExprSimplify", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G501", + "G502", + "G503", + "G504", + "G505", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite" + ] + }, + "linters": { + "$comment": "anyOf with enum is used to allow auto completion of non-custom linters", + "description": "Linters usable.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "deadcode", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "execinquery", + "exhaustive", + "exhaustivestruct", + "exhaustruct", + "exportloopref", + "forbidigo", + "forcetypeassert", + "funlen", + "gci", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "goerr113", + "gofmt", + "gofumpt", + "goheader", + "goimports", + "golint", + "gomnd", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "ifshort", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "interfacer", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "maligned", + "mirror", + "misspell", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosnakecase", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "revive", + "rowserrcheck", + "scopelint", + "sloglint", + "sqlclosecheck", + "staticcheck", + "structcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "tenv", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "typecheck", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "varcheck", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "default": "stdout", + "anyOf": [ + { + "enum": [ "stdout", "stderr" ] + }, + { + "type": "string" + } + ] + }, + "format": { + "default": "colored-line-number", + "enum": [ + "colored-line-number", + "line-number", + "json", + "colored-tab", + "tab", + "checkstyle", + "code-climate", + "junit-xml", + "github-actions", + "teamcity" + ] + } + }, + "required": ["format"] + } + }, + "print-issued-lines": { + "description": "Print lines of code with issue.", + "type": "boolean", + "default": true + }, + "print-linter-name": { + "description": "Print linter name in the end of issue text.", + "type": "boolean", + "default": true + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": false + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + }, + "sort-results": { + "description": "Sort results by: filepath, line and column.", + "type": "boolean", + "default": true + } + } + }, + "linters-settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalint": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + }, + "ignore-test": { + "description": "Ignore *_test.go files.", + "type": "boolean", + "default": false + } + } + }, + "bidichk": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclop": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-tests": { + "description": "Should the linter execute on test files as well", + "type": "boolean", + "default": false + }, + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorder": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsled": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "dupl": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "ignore": { + "description": "DEPRECATED: use `exclude-functions` instead. Comma-separated list of pairs of the form \"pkg:regex\".", + "type": "string", + "default": "fmt:.*" + }, + "exclude": { + "description": "DEPRECATED: use `exclude-functions` instead. Path to a file containing a list of functions to exclude from checking.", + "type": "string", + "examples": ["/path/to/file.txt"] + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjson": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + } + } + }, + "exhaustive": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "check-generated": { + "description": "Check switch statements in generated files", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustruct": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "forbidigo": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "examples": ["^print.*$"], + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "p": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + ] + } + } + } + }, + "funlen": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": false + } + } + }, + "gci": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "DEPRECATED: use 'sections' and 'prefix(github.com/org/project)' instead.", + "type": "string", + "examples": ["github.com/org/project"] + }, + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["standard", "default"] + }, + "skip-generated": { + "description": "Skip generated files.", + "type": "boolean", + "default": true + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + } + } + }, + "gocognit": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconst": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-strings": { + "description": "Exclude strings matching the given regular expression", + "type": "string" + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + } + } + }, + "gocritic": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocyclo": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godot": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godox": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmt": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "interfacebloat": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumpt": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "lang-version": { + "description": "Select the Go version to target.", + "type": "string", + "default": "1.15" + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheader": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimports": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a comma-separated list of prefixes.", + "type": "string", + "examples": ["github.com/org/project"] + } + } + }, + "gomnd": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "gomoddirectives": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": true + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": true + } + } + }, + "gomodguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local_replace_directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosimple": { + "type": "object", + "additionalProperties": false, + "properties": { + "go": { + "description": "Targeted Go version", + "type": "string", + "default": "1.13" + }, + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "gosec": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "exclude-generated": { + "description": "Exclude generated files", + "type": "boolean", + "default": false + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitan": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govet": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouper": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "importas": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturn": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lll": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidx": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezero": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspell": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-words": { + "description": "List of words to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttag": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedret": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestif": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnil": { + "type": "object", + "additionalProperties": false, + "properties": { + "checked-types": { + "type": "array", + "description": "Order of return types to check.", + "items": { + "enum": ["ptr", "func", "iface", "map", "chan"] + }, + "default": ["ptr", "func", "iface", "map", "chan"] + } + } + }, + "nlreturn": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "nolintlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassign": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturns": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltest": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprint": { + "type": "object", + "additionalProperties": false, + "properties": { + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + } + } + }, + "prealloc": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclared": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "Comma-separated list of predeclared identifiers to not report on.", + "type": "string" + }, + "q": { + "description": "Include method names and field names (i.e., qualified names) in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetter": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "revive": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "ignore-generated-header": { + "type": "boolean" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "type": "string", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context-only": { + "description": "Enforce using methods that accept a context.", + "type": "boolean", + "default": false + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "go": { + "description": "Targeted Go version", + "type": "string", + "default": "1.13" + }, + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "stylecheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "go": { + "description": "Targeted Go version", + "type": "string", + "default": "1.13" + }, + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + }, + "default": [ + "all", + "-ST1000", + "-ST1003", + "-ST1016", + "-ST1020", + "-ST1021", + "-ST1022" + ] + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalign": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelle": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "enum": [ + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + } + } + } + } + } + } + }, + "tenv": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "description": "The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.", + "type": "boolean", + "default": false + } + } + }, + "testifylint": { + "type": "object", + "additionalProperties": false, + "properties": { + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "type": "boolean", + "default": false + } + } + }, + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "go-require", + "float-compare", + "len", + "nil-compare", + "require-error", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-thelper", + "useless-assert" + ] + } + }, + "disable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "go-require", + "float-compare", + "len", + "nil-compare", + "require-error", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-thelper", + "useless-assert" + ] + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string" + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for expected variable name.", + "type": "string" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"] + } + } + } + } + }, + "testpackage": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelper": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvars": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "os-dev-null": { + "description": "Suggest the use of os.DevNull.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + }, + "syslog-priority": { + "description": "Suggest the use of syslog.Priority.", + "type": "boolean", + "default": false + } + } + }, + "unconvert": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-is-used": { + "description": "", + "type": "boolean", + "default": true + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelen": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespace": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignoreSigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigRegexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignorePackageGlobs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreInterfaceRegexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wsl": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvar": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-alias": { + "type": "boolean", + "default": false + } + } + }, + "custom": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "enable-all": { + "description": "Whether to enable all linters. You can re-disable them with `disable` explicitly.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Whether to disable all linters. You can re-enable them with `enable` explicitly.", + "type": "boolean", + "default": false + }, + "presets": { + "description": "Allow to use different presets of linters", + "type": "array", + "items": { + "enum": [ + "bugs", + "comment", + "complexity", + "error", + "format", + "import", + "metalinter", + "module", + "performance", + "sql", + "style", + "test", + "unused" + ] + } + }, + "fast": { + "description": "Enable run of fast linters.", + "type": "boolean", + "default": false + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "List of regular expressions of issue texts to exclude.\nBut independently from this option we use default exclude patterns. Their usage can be controlled through `exclude-use-default`.", + "type": "array", + "items": { + "type": "string" + } + }, + "exclude-rules": { + "description": "Exclude configuration per-path, per-linter, per-text and per-source", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "exclude-use-default": { + "description": "Independently from option `exclude` we use default exclude patterns. This behavior can be disabled by this option.", + "type": "boolean", + "default": true + }, + "exclude-case-sensitive": { + "description": "If set to true, exclude and exclude-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "exclude-generated-strict": { + "description": "To follow strict Go generated file convention", + "type": "boolean", + "default": false + }, + "exclude-dirs": { + "description": "Which directories to exclude: issues from them won't be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. The regexp is applied on the full path.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": ["generated.*"] + }, + "default": [], + "examples": [["src/external_libs", "autogenerated_by_my_lib"]] + }, + "exclude-dirs-use-default": { + "description": "Enable exclusion of directories \"vendor\", \"third_party\", \"testdata\", \"examples\", \"Godeps\", and \"builtin\".", + "type": "boolean", + "default": true + }, + "exclude-files": { + "description": "Which files to exclude: they will be analyzed, but issues from them will not be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. There is no need to include all autogenerated files, we confidently recognize them. If that is not the case, please let us know.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": [".*\\.my\\.go$"] + }, + "default": [], + "examples": [[".*\\.my\\.go$", "lib/bad.go"]] + }, + "include": { + "description": "The list of ids of default excludes to include or disable.", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-severity": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "case-sensitive": { + "description": "If set to true, severity-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default-severity"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.58.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.58.jsonschema.json new file mode 100644 index 00000000..d0bdc8d7 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.58.jsonschema.json @@ -0,0 +1,3638 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentedOutCode", + "commentedOutImport", + "commentFormatting", + "defaultCaseOrder", + "deferUnlambda", + "deferInLoop", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringsCompare", + "stringXbytes", + "suspiciousSorting", + "switchTrue", + "syncMapLoadAndDelete", + "timeCmpSimplify", + "timeExprSimplify", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G501", + "G502", + "G503", + "G504", + "G505", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite" + ] + }, + "linters": { + "$comment": "anyOf with enum is used to allow auto completion of non-custom linters", + "description": "Linters usable.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "deadcode", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "execinquery", + "exhaustive", + "exhaustivestruct", + "exhaustruct", + "exportloopref", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funlen", + "gci", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "gofmt", + "gofumpt", + "goheader", + "goimports", + "golint", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "ifshort", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "interfacer", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "maligned", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosnakecase", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "revive", + "rowserrcheck", + "scopelint", + "sloglint", + "sqlclosecheck", + "staticcheck", + "structcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "tenv", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "typecheck", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "varcheck", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "default": "stdout", + "anyOf": [ + { + "enum": [ "stdout", "stderr" ] + }, + { + "type": "string" + } + ] + }, + "format": { + "default": "colored-line-number", + "enum": [ + "colored-line-number", + "line-number", + "json", + "colored-tab", + "tab", + "html", + "checkstyle", + "code-climate", + "junit-xml", + "github-actions", + "teamcity" + ] + } + }, + "required": ["format"] + } + }, + "print-issued-lines": { + "description": "Print lines of code with issue.", + "type": "boolean", + "default": true + }, + "print-linter-name": { + "description": "Print linter name in the end of issue text.", + "type": "boolean", + "default": true + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": false + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + }, + "sort-results": { + "description": "Sort results by: filepath, line and column.", + "type": "boolean", + "default": true + } + } + }, + "linters-settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalint": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + }, + "ignore-test": { + "description": "Ignore *_test.go files.", + "type": "boolean", + "default": false + } + } + }, + "bidichk": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclop": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-tests": { + "description": "Should the linter execute on test files as well", + "type": "boolean", + "default": false + }, + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorder": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsled": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "dupl": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjson": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustive": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "check-generated": { + "description": "Check switch statements in generated files", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustruct": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "forbidigo": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "examples": ["^print.*$"], + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "p": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + ] + } + } + } + }, + "funlen": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": false + } + } + }, + "gci": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "skip-generated": { + "description": "Skip generated files.", + "type": "boolean", + "default": true + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + } + } + }, + "gocognit": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconst": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-strings": { + "description": "Exclude strings matching the given regular expression", + "type": "string" + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + } + } + }, + "gocritic": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocyclo": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godot": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godox": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmt": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "interfacebloat": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumpt": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheader": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimports": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a comma-separated list of prefixes.", + "type": "string", + "examples": ["github.com/org/project"] + } + } + }, + "gomoddirectives": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": true + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": true + } + } + }, + "gomodguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local_replace_directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosimple": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "gosec": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "exclude-generated": { + "description": "Exclude generated files", + "type": "boolean", + "default": false + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitan": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govet": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouper": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "importas": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturn": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lll": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidx": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezero": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspell": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-words": { + "description": "List of words to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttag": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedret": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestif": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnil": { + "type": "object", + "additionalProperties": false, + "properties": { + "checked-types": { + "type": "array", + "description": "Order of return types to check.", + "items": { + "enum": ["ptr", "func", "iface", "map", "chan"] + }, + "default": ["ptr", "func", "iface", "map", "chan"] + } + } + }, + "nlreturn": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mnd": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassign": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturns": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltest": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprint": { + "type": "object", + "additionalProperties": false, + "properties": { + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + } + } + }, + "prealloc": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclared": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "Comma-separated list of predeclared identifiers to not report on.", + "type": "string" + }, + "q": { + "description": "Include method names and field names (i.e., qualified names) in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetter": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "revive": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "ignore-generated-header": { + "type": "boolean" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "type": "string", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "stylecheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + }, + "default": [ + "all", + "-ST1000", + "-ST1003", + "-ST1016", + "-ST1020", + "-ST1021", + "-ST1022" + ] + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalign": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelle": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "enum": [ + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + } + } + } + } + } + } + }, + "tenv": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "description": "The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.", + "type": "boolean", + "default": false + } + } + }, + "testifylint": { + "type": "object", + "additionalProperties": false, + "properties": { + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "type": "boolean", + "default": false + } + } + }, + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "go-require", + "float-compare", + "len", + "nil-compare", + "require-error", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-thelper", + "useless-assert" + ] + } + }, + "disable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "go-require", + "float-compare", + "len", + "nil-compare", + "require-error", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-thelper", + "useless-assert" + ] + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string" + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for expected variable name.", + "type": "string" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"] + } + } + } + } + }, + "testpackage": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelper": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvars": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "unconvert": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-is-used": { + "description": "", + "type": "boolean", + "default": true + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelen": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespace": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignoreSigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigRegexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignorePackageGlobs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreInterfaceRegexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wsl": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvar": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "custom": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "enable-all": { + "description": "Whether to enable all linters. You can re-disable them with `disable` explicitly.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Whether to disable all linters. You can re-enable them with `enable` explicitly.", + "type": "boolean", + "default": false + }, + "presets": { + "description": "Allow to use different presets of linters", + "type": "array", + "items": { + "enum": [ + "bugs", + "comment", + "complexity", + "error", + "format", + "import", + "metalinter", + "module", + "performance", + "sql", + "style", + "test", + "unused" + ] + } + }, + "fast": { + "description": "Enable run of fast linters.", + "type": "boolean", + "default": false + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "List of regular expressions of issue texts to exclude.\nBut independently from this option we use default exclude patterns. Their usage can be controlled through `exclude-use-default`.", + "type": "array", + "items": { + "type": "string" + } + }, + "exclude-rules": { + "description": "Exclude configuration per-path, per-linter, per-text and per-source", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "exclude-use-default": { + "description": "Independently from option `exclude` we use default exclude patterns. This behavior can be disabled by this option.", + "type": "boolean", + "default": true + }, + "exclude-case-sensitive": { + "description": "If set to true, exclude and exclude-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "exclude-generated-strict": { + "description": "To follow strict Go generated file convention", + "type": "boolean", + "default": false + }, + "exclude-dirs": { + "description": "Which directories to exclude: issues from them won't be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. The regexp is applied on the full path.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": ["generated.*"] + }, + "default": [], + "examples": [["src/external_libs", "autogenerated_by_my_lib"]] + }, + "exclude-dirs-use-default": { + "description": "Enable exclusion of directories \"vendor\", \"third_party\", \"testdata\", \"examples\", \"Godeps\", and \"builtin\".", + "type": "boolean", + "default": true + }, + "exclude-files": { + "description": "Which files to exclude: they will be analyzed, but issues from them will not be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. There is no need to include all autogenerated files, we confidently recognize them. If that is not the case, please let us know.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": [".*\\.my\\.go$"] + }, + "default": [], + "examples": [[".*\\.my\\.go$", "lib/bad.go"]] + }, + "include": { + "description": "The list of ids of default excludes to include or disable.", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-severity": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "case-sensitive": { + "description": "If set to true, severity-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default-severity"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.59.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.59.jsonschema.json new file mode 100644 index 00000000..cc3e6365 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.59.jsonschema.json @@ -0,0 +1,3766 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentedOutCode", + "commentedOutImport", + "commentFormatting", + "defaultCaseOrder", + "deferUnlambda", + "deferInLoop", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringsCompare", + "stringXbytes", + "suspiciousSorting", + "switchTrue", + "syncMapLoadAndDelete", + "timeCmpSimplify", + "timeExprSimplify", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G501", + "G502", + "G503", + "G504", + "G505", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range", + "range-val-address", + "range-val-in-closure", + "receiver-naming", + "redefines-builtin-id", + "redundant-import-alias", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "linters": { + "$comment": "anyOf with enum is used to allow auto completion of non-custom linters", + "description": "Linters usable.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "deadcode", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "execinquery", + "exhaustive", + "exhaustivestruct", + "exhaustruct", + "exportloopref", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funlen", + "gci", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "gofmt", + "gofumpt", + "goheader", + "goimports", + "golint", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "ifshort", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "interfacer", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "maligned", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosnakecase", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "revive", + "rowserrcheck", + "scopelint", + "sloglint", + "sqlclosecheck", + "staticcheck", + "structcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "tenv", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "typecheck", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "varcheck", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "default": "stdout", + "anyOf": [ + { + "enum": [ "stdout", "stderr" ] + }, + { + "type": "string" + } + ] + }, + "format": { + "default": "colored-line-number", + "enum": [ + "colored-line-number", + "line-number", + "json", + "colored-tab", + "tab", + "html", + "checkstyle", + "code-climate", + "junit-xml", + "github-actions", + "teamcity", + "sarif" + ] + } + }, + "required": ["format"] + } + }, + "print-issued-lines": { + "description": "Print lines of code with issue.", + "type": "boolean", + "default": true + }, + "print-linter-name": { + "description": "Print linter name in the end of issue text.", + "type": "boolean", + "default": true + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": false + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + }, + "sort-results": { + "description": "Sort results by: filepath, line and column.", + "type": "boolean", + "default": true + } + } + }, + "linters-settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalint": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + }, + "ignore-test": { + "description": "Ignore *_test.go files.", + "type": "boolean", + "default": false + } + } + }, + "bidichk": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclop": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-tests": { + "description": "Should the linter execute on test files as well", + "type": "boolean", + "default": false + }, + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorder": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsled": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "dupl": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjson": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustive": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "check-generated": { + "description": "Check switch statements in generated files", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustruct": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "forbidigo": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "examples": ["^print.*$"], + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "p": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + ] + } + } + } + }, + "funlen": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": false + } + } + }, + "gci": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "skip-generated": { + "description": "Skip generated files.", + "type": "boolean", + "default": true + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + } + } + }, + "gocognit": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconst": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-strings": { + "description": "Exclude strings matching the given regular expression", + "type": "string" + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + } + } + }, + "gocritic": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocyclo": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godot": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godox": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmt": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "interfacebloat": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumpt": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheader": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimports": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a comma-separated list of prefixes.", + "type": "string", + "examples": ["github.com/org/project"] + } + } + }, + "gomoddirectives": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": true + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": true + } + } + }, + "gomodguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local_replace_directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosimple": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "gosec": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "exclude-generated": { + "description": "Exclude generated files", + "type": "boolean", + "default": false + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitan": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govet": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouper": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "importas": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturn": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lll": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidx": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezero": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspell": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-words": { + "description": "List of words to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttag": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedret": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestif": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnil": { + "type": "object", + "additionalProperties": false, + "properties": { + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["ptr", "func", "iface", "map", "chan", "uintptr", "unsafeptr"] + }, + "default": ["ptr", "func", "iface", "map", "chan", "uintptr", "unsafeptr"] + } + } + }, + "nlreturn": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mnd": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassign": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturns": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltest": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprint": { + "type": "object", + "additionalProperties": false, + "properties": { + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + } + } + }, + "prealloc": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclared": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "Comma-separated list of predeclared identifiers to not report on.", + "type": "string" + }, + "q": { + "description": "Include method names and field names (i.e., qualified names) in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetter": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "revive": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "ignore-generated-header": { + "type": "boolean" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "stylecheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + }, + "default": [ + "all", + "-ST1000", + "-ST1003", + "-ST1016", + "-ST1020", + "-ST1021", + "-ST1022" + ] + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalign": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelle": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "enum": [ + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + } + } + } + } + } + } + }, + "tenv": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "description": "The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.", + "type": "boolean", + "default": false + } + } + }, + "testifylint": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "go-require", + "len", + "negative-positive", + "nil-compare", + "require-error", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "go-require", + "len", + "negative-positive", + "nil-compare", + "require-error", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "go-require", + "len", + "negative-positive", + "nil-compare", + "require-error", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackage": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelper": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvars": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "unconvert": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-is-used": { + "description": "", + "type": "boolean", + "default": true + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelen": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespace": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignoreSigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigRegexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignorePackageGlobs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreInterfaceRegexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wsl": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvar": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "custom": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "enable-all": { + "description": "Whether to enable all linters. You can re-disable them with `disable` explicitly.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Whether to disable all linters. You can re-enable them with `enable` explicitly.", + "type": "boolean", + "default": false + }, + "presets": { + "description": "Allow to use different presets of linters", + "type": "array", + "items": { + "enum": [ + "bugs", + "comment", + "complexity", + "error", + "format", + "import", + "metalinter", + "module", + "performance", + "sql", + "style", + "test", + "unused" + ] + } + }, + "fast": { + "description": "Enable run of fast linters.", + "type": "boolean", + "default": false + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "List of regular expressions of issue texts to exclude.\nBut independently from this option we use default exclude patterns. Their usage can be controlled through `exclude-use-default`.", + "type": "array", + "items": { + "type": "string" + } + }, + "exclude-rules": { + "description": "Exclude configuration per-path, per-linter, per-text and per-source", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "exclude-use-default": { + "description": "Independently from option `exclude` we use default exclude patterns. This behavior can be disabled by this option.", + "type": "boolean", + "default": true + }, + "exclude-case-sensitive": { + "description": "If set to true, exclude and exclude-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "exclude-generated": { + "description": "Mode of the generated files analysis.", + "enum": ["lax", "strict", "disable"], + "default": "lax" + }, + "exclude-dirs": { + "description": "Which directories to exclude: issues from them won't be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. The regexp is applied on the full path.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": ["generated.*"] + }, + "default": [], + "examples": [["src/external_libs", "autogenerated_by_my_lib"]] + }, + "exclude-dirs-use-default": { + "description": "Enable exclusion of directories \"vendor\", \"third_party\", \"testdata\", \"examples\", \"Godeps\", and \"builtin\".", + "type": "boolean", + "default": true + }, + "exclude-files": { + "description": "Which files to exclude: they will be analyzed, but issues from them will not be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. There is no need to include all autogenerated files, we confidently recognize them. If that is not the case, please let us know.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": [".*\\.my\\.go$"] + }, + "default": [], + "examples": [[".*\\.my\\.go$", "lib/bad.go"]] + }, + "include": { + "description": "The list of ids of default excludes to include or disable.", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-severity": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "case-sensitive": { + "description": "If set to true, severity-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default-severity"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.60.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.60.jsonschema.json new file mode 100644 index 00000000..b8920ae0 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.60.jsonschema.json @@ -0,0 +1,3792 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentedOutCode", + "commentedOutImport", + "commentFormatting", + "defaultCaseOrder", + "deferUnlambda", + "deferInLoop", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringsCompare", + "stringXbytes", + "suspiciousSorting", + "switchTrue", + "syncMapLoadAndDelete", + "timeCmpSimplify", + "timeExprSimplify", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range", + "range-val-address", + "range-val-in-closure", + "receiver-naming", + "redefines-builtin-id", + "redundant-import-alias", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "linters": { + "$comment": "anyOf with enum is used to allow auto completion of non-custom linters", + "description": "Linters usable.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "deadcode", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "execinquery", + "exhaustive", + "exhaustivestruct", + "exhaustruct", + "exportloopref", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funlen", + "gci", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "gofmt", + "gofumpt", + "goheader", + "goimports", + "golint", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "ifshort", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "interfacer", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "maligned", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosnakecase", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "revive", + "rowserrcheck", + "scopelint", + "sloglint", + "sqlclosecheck", + "staticcheck", + "structcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "tenv", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "typecheck", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "varcheck", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "default": "stdout", + "anyOf": [ + { + "enum": [ "stdout", "stderr" ] + }, + { + "type": "string" + } + ] + }, + "format": { + "default": "colored-line-number", + "enum": [ + "colored-line-number", + "line-number", + "json", + "colored-tab", + "tab", + "html", + "checkstyle", + "code-climate", + "junit-xml", + "github-actions", + "teamcity", + "sarif" + ] + } + }, + "required": ["format"] + } + }, + "print-issued-lines": { + "description": "Print lines of code with issue.", + "type": "boolean", + "default": true + }, + "print-linter-name": { + "description": "Print linter name in the end of issue text.", + "type": "boolean", + "default": true + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": false + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + }, + "sort-results": { + "description": "Sort results by: filepath, line and column.", + "type": "boolean", + "default": true + } + } + }, + "linters-settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalint": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + }, + "ignore-test": { + "description": "Ignore *_test.go files.", + "type": "boolean", + "default": false + } + } + }, + "bidichk": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclop": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-tests": { + "description": "Should the linter execute on test files as well", + "type": "boolean", + "default": false + }, + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorder": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsled": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "dupl": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjson": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustive": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "check-generated": { + "description": "Check switch statements in generated files", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustruct": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "forbidigo": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "examples": ["^print.*$"], + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "p": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + ] + } + } + } + }, + "funlen": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": false + } + } + }, + "gci": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "skip-generated": { + "description": "Skip generated files.", + "type": "boolean", + "default": true + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + } + } + }, + "gocognit": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconst": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-strings": { + "description": "Exclude strings matching the given regular expression", + "type": "string" + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + } + } + }, + "gocritic": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocyclo": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godot": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godox": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmt": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "interfacebloat": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumpt": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheader": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimports": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a comma-separated list of prefixes.", + "type": "string", + "examples": ["github.com/org/project"] + } + } + }, + "gomoddirectives": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": true + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": true + } + } + }, + "gomodguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local_replace_directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosimple": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "gosec": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "exclude-generated": { + "description": "Exclude generated files", + "type": "boolean", + "default": false + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitan": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govet": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouper": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "importas": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturn": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lll": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidx": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezero": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspell": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-words": { + "description": "List of words to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttag": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedret": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestif": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnil": { + "type": "object", + "additionalProperties": false, + "properties": { + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["ptr", "func", "iface", "map", "chan", "uintptr", "unsafeptr"] + }, + "default": ["ptr", "func", "iface", "map", "chan", "uintptr", "unsafeptr"] + } + } + }, + "nlreturn": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mnd": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassign": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturns": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltest": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprint": { + "type": "object", + "additionalProperties": false, + "properties": { + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + } + } + }, + "prealloc": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclared": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "Comma-separated list of predeclared identifiers to not report on.", + "type": "string" + }, + "q": { + "description": "Include method names and field names (i.e., qualified names) in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetter": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "revive": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "ignore-generated-header": { + "type": "boolean" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "stylecheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + }, + "default": [ + "all", + "-ST1000", + "-ST1003", + "-ST1016", + "-ST1020", + "-ST1021", + "-ST1022" + ] + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalign": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelle": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "enum": [ + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + } + } + } + } + } + } + }, + "tenv": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "description": "The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.", + "type": "boolean", + "default": false + } + } + }, + "testifylint": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "float-compare", + "go-require", + "len", + "negative-positive", + "nil-compare", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions if format string is used.", + "type": "boolean", + "default": false + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackage": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelper": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvars": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "unconvert": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelen": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespace": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignoreSigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigRegexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignorePackageGlobs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreInterfaceRegexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wsl": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvar": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "custom": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "enable-all": { + "description": "Whether to enable all linters. You can re-disable them with `disable` explicitly.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Whether to disable all linters. You can re-enable them with `enable` explicitly.", + "type": "boolean", + "default": false + }, + "presets": { + "description": "Allow to use different presets of linters", + "type": "array", + "items": { + "enum": [ + "bugs", + "comment", + "complexity", + "error", + "format", + "import", + "metalinter", + "module", + "performance", + "sql", + "style", + "test", + "unused" + ] + } + }, + "fast": { + "description": "Enable run of fast linters.", + "type": "boolean", + "default": false + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "List of regular expressions of issue texts to exclude.\nBut independently from this option we use default exclude patterns. Their usage can be controlled through `exclude-use-default`.", + "type": "array", + "items": { + "type": "string" + } + }, + "exclude-rules": { + "description": "Exclude configuration per-path, per-linter, per-text and per-source", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "exclude-use-default": { + "description": "Independently from option `exclude` we use default exclude patterns. This behavior can be disabled by this option.", + "type": "boolean", + "default": true + }, + "exclude-case-sensitive": { + "description": "If set to true, exclude and exclude-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "exclude-generated": { + "description": "Mode of the generated files analysis.", + "enum": ["lax", "strict", "disable"], + "default": "lax" + }, + "exclude-dirs": { + "description": "Which directories to exclude: issues from them won't be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. The regexp is applied on the full path.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": ["generated.*"] + }, + "default": [], + "examples": [["src/external_libs", "autogenerated_by_my_lib"]] + }, + "exclude-dirs-use-default": { + "description": "Enable exclusion of directories \"vendor\", \"third_party\", \"testdata\", \"examples\", \"Godeps\", and \"builtin\".", + "type": "boolean", + "default": true + }, + "exclude-files": { + "description": "Which files to exclude: they will be analyzed, but issues from them will not be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. There is no need to include all autogenerated files, we confidently recognize them. If that is not the case, please let us know.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": [".*\\.my\\.go$"] + }, + "default": [], + "examples": [[".*\\.my\\.go$", "lib/bad.go"]] + }, + "include": { + "description": "The list of ids of default excludes to include or disable.", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-severity": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "case-sensitive": { + "description": "If set to true, severity-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default-severity"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.61.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.61.jsonschema.json new file mode 100644 index 00000000..a574b019 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.61.jsonschema.json @@ -0,0 +1,3797 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentedOutCode", + "commentedOutImport", + "commentFormatting", + "defaultCaseOrder", + "deferUnlambda", + "deferInLoop", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringsCompare", + "stringXbytes", + "suspiciousSorting", + "switchTrue", + "syncMapLoadAndDelete", + "timeCmpSimplify", + "timeExprSimplify", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range", + "range-val-address", + "range-val-in-closure", + "receiver-naming", + "redefines-builtin-id", + "redundant-import-alias", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "linters": { + "$comment": "anyOf with enum is used to allow auto completion of non-custom linters", + "description": "Linters usable.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "deadcode", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "execinquery", + "exhaustive", + "exhaustivestruct", + "exhaustruct", + "exportloopref", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funlen", + "gci", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "gofmt", + "gofumpt", + "goheader", + "goimports", + "golint", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "ifshort", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "interfacer", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "maligned", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosnakecase", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "revive", + "rowserrcheck", + "scopelint", + "sloglint", + "sqlclosecheck", + "staticcheck", + "structcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "tenv", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "varcheck", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "default": "stdout", + "anyOf": [ + { + "enum": [ "stdout", "stderr" ] + }, + { + "type": "string" + } + ] + }, + "format": { + "default": "colored-line-number", + "enum": [ + "colored-line-number", + "line-number", + "json", + "colored-tab", + "tab", + "html", + "checkstyle", + "code-climate", + "junit-xml", + "junit-xml-extended", + "github-actions", + "teamcity", + "sarif" + ] + } + }, + "required": ["format"] + } + }, + "print-issued-lines": { + "description": "Print lines of code with issue.", + "type": "boolean", + "default": true + }, + "print-linter-name": { + "description": "Print linter name in the end of issue text.", + "type": "boolean", + "default": true + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": false + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + }, + "sort-results": { + "description": "Sort results by: filepath, line and column.", + "type": "boolean", + "default": true + } + } + }, + "linters-settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalint": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + }, + "ignore-test": { + "description": "Ignore *_test.go files.", + "type": "boolean", + "default": false + } + } + }, + "bidichk": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclop": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-tests": { + "description": "Should the linter execute on test files as well", + "type": "boolean", + "default": false + }, + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorder": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsled": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "dupl": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjson": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustive": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "check-generated": { + "description": "Check switch statements in generated files", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustruct": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "forbidigo": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "examples": ["^print(ln)?$"], + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "p": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + ] + } + } + } + }, + "funlen": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": false + } + } + }, + "gci": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "skip-generated": { + "description": "Skip generated files.", + "type": "boolean", + "default": true + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + } + } + }, + "gocognit": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconst": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-strings": { + "description": "Exclude strings matching the given regular expression", + "type": "string" + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + } + } + }, + "gocritic": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocyclo": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godot": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godox": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmt": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "interfacebloat": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumpt": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheader": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimports": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a comma-separated list of prefixes.", + "type": "string", + "examples": ["github.com/org/project"] + } + } + }, + "gomoddirectives": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": true + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": true + } + } + }, + "gomodguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local_replace_directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosimple": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "gosec": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "exclude-generated": { + "description": "Exclude generated files", + "type": "boolean", + "default": false + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitan": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govet": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouper": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "importas": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturn": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lll": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidx": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezero": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspell": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-words": { + "description": "List of words to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttag": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedret": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestif": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnil": { + "type": "object", + "additionalProperties": false, + "properties": { + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["ptr", "func", "iface", "map", "chan", "uintptr", "unsafeptr"] + }, + "default": ["ptr", "func", "iface", "map", "chan", "uintptr", "unsafeptr"] + } + } + }, + "nlreturn": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mnd": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassign": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturns": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltest": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprint": { + "type": "object", + "additionalProperties": false, + "properties": { + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + } + } + }, + "prealloc": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclared": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "Comma-separated list of predeclared identifiers to not report on.", + "type": "string" + }, + "q": { + "description": "Include method names and field names (i.e., qualified names) in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetter": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "revive": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "ignore-generated-header": { + "type": "boolean" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "stylecheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + }, + "default": [ + "all", + "-ST1000", + "-ST1003", + "-ST1016", + "-ST1020", + "-ST1021", + "-ST1022" + ] + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalign": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelle": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "enum": [ + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + } + } + } + } + } + } + }, + "tenv": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "description": "The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.", + "type": "boolean", + "default": false + } + } + }, + "testifylint": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "float-compare", + "go-require", + "len", + "negative-positive", + "nil-compare", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "empty", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions if format string is used.", + "type": "boolean", + "default": false + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackage": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelper": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvars": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "unconvert": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelen": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespace": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignoreSigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigRegexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignorePackageGlobs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreInterfaceRegexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wsl": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvar": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "custom": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "enable-all": { + "description": "Whether to enable all linters. You can re-disable them with `disable` explicitly.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Whether to disable all linters. You can re-enable them with `enable` explicitly.", + "type": "boolean", + "default": false + }, + "presets": { + "description": "Allow to use different presets of linters", + "type": "array", + "items": { + "enum": [ + "bugs", + "comment", + "complexity", + "error", + "format", + "import", + "metalinter", + "module", + "performance", + "sql", + "style", + "test", + "unused" + ] + } + }, + "fast": { + "description": "Enable run of fast linters.", + "type": "boolean", + "default": false + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "List of regular expressions of issue texts to exclude.\nBut independently from this option we use default exclude patterns. Their usage can be controlled through `exclude-use-default`.", + "type": "array", + "items": { + "type": "string" + } + }, + "exclude-rules": { + "description": "Exclude configuration per-path, per-linter, per-text and per-source", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "exclude-use-default": { + "description": "Independently from option `exclude` we use default exclude patterns. This behavior can be disabled by this option.", + "type": "boolean", + "default": true + }, + "exclude-case-sensitive": { + "description": "If set to true, exclude and exclude-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "exclude-generated": { + "description": "Mode of the generated files analysis.", + "enum": ["lax", "strict", "disable"], + "default": "lax" + }, + "exclude-dirs": { + "description": "Which directories to exclude: issues from them won't be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. The regexp is applied on the full path.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": ["generated.*"] + }, + "default": [], + "examples": [["src/external_libs", "autogenerated_by_my_lib"]] + }, + "exclude-dirs-use-default": { + "description": "Enable exclusion of directories \"vendor\", \"third_party\", \"testdata\", \"examples\", \"Godeps\", and \"builtin\".", + "type": "boolean", + "default": true + }, + "exclude-files": { + "description": "Which files to exclude: they will be analyzed, but issues from them will not be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. There is no need to include all autogenerated files, we confidently recognize them. If that is not the case, please let us know.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": [".*\\.my\\.go$"] + }, + "default": [], + "examples": [[".*\\.my\\.go$", "lib/bad.go"]] + }, + "include": { + "description": "The list of ids of default excludes to include or disable.", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-severity": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "case-sensitive": { + "description": "If set to true, severity-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default-severity"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.62.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.62.jsonschema.json new file mode 100644 index 00000000..3ca3593b --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.62.jsonschema.json @@ -0,0 +1,3860 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range", + "range-val-address", + "range-val-in-closure", + "receiver-naming", + "redefines-builtin-id", + "redundant-import-alias", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque" + ] + }, + "linters": { + "$comment": "anyOf with enum is used to allow auto completion of non-custom linters", + "description": "Linters usable.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exportloopref", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funlen", + "gci", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "gofmt", + "gofumpt", + "goheader", + "goimports", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "tenv", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "default": "stdout", + "anyOf": [ + { + "enum": [ "stdout", "stderr" ] + }, + { + "type": "string" + } + ] + }, + "format": { + "default": "colored-line-number", + "enum": [ + "colored-line-number", + "line-number", + "json", + "colored-tab", + "tab", + "html", + "checkstyle", + "code-climate", + "junit-xml", + "junit-xml-extended", + "github-actions", + "teamcity", + "sarif" + ] + } + }, + "required": ["format"] + } + }, + "print-issued-lines": { + "description": "Print lines of code with issue.", + "type": "boolean", + "default": true + }, + "print-linter-name": { + "description": "Print linter name in the end of issue text.", + "type": "boolean", + "default": true + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": false + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + }, + "sort-results": { + "description": "Sort results by: filepath, line and column.", + "type": "boolean", + "default": true + } + } + }, + "linters-settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalint": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + }, + "ignore-test": { + "description": "Ignore *_test.go files.", + "type": "boolean", + "default": false + } + } + }, + "bidichk": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclop": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-tests": { + "description": "Should the linter execute on test files as well", + "type": "boolean", + "default": false + }, + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorder": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsled": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "dupl": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjson": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustive": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "check-generated": { + "description": "Check switch statements in generated files", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustruct": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "forbidigo": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "examples": ["^print(ln)?$"], + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "p": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + ] + } + } + } + }, + "funlen": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": false + } + } + }, + "gci": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "skip-generated": { + "description": "Skip generated files.", + "type": "boolean", + "default": true + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtype": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + } + } + }, + "gocognit": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconst": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-strings": { + "description": "Exclude strings matching the given regular expression", + "type": "string" + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + } + } + }, + "gocritic": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocyclo": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godot": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godox": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmt": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "interfacebloat": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumpt": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheader": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimports": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a comma-separated list of prefixes.", + "type": "string", + "examples": ["github.com/org/project"] + } + } + }, + "gomoddirectives": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": false + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + } + } + }, + "gomodguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local_replace_directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosimple": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "gosec": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "exclude-generated": { + "description": "Exclude generated files", + "type": "boolean", + "default": false + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitan": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govet": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouper": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "iface": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importas": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturn": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lll": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidx": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezero": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspell": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-words": { + "description": "List of words to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttag": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedret": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestif": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnil": { + "type": "object", + "additionalProperties": false, + "properties": { + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturn": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mnd": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassign": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturns": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltest": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprint": { + "type": "object", + "additionalProperties": false, + "properties": { + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + } + } + }, + "prealloc": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclared": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "Comma-separated list of predeclared identifiers to not report on.", + "type": "string" + }, + "q": { + "description": "Include method names and field names (i.e., qualified names) in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetter": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "revive": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "ignore-generated-header": { + "type": "boolean" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "stylecheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + }, + "default": [ + "all", + "-ST1000", + "-ST1003", + "-ST1016", + "-ST1020", + "-ST1021", + "-ST1022" + ] + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalign": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelle": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "enum": [ + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + } + } + } + } + } + } + }, + "tenv": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "description": "The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.", + "type": "boolean", + "default": false + } + } + }, + "testifylint": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackage": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelper": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvars": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "unconvert": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelen": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespace": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignoreSigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigRegexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignorePackageGlobs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreInterfaceRegexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wsl": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvar": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "custom": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "enable-all": { + "description": "Whether to enable all linters. You can re-disable them with `disable` explicitly.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Whether to disable all linters. You can re-enable them with `enable` explicitly.", + "type": "boolean", + "default": false + }, + "presets": { + "description": "Allow to use different presets of linters", + "type": "array", + "items": { + "enum": [ + "bugs", + "comment", + "complexity", + "error", + "format", + "import", + "metalinter", + "module", + "performance", + "sql", + "style", + "test", + "unused" + ] + } + }, + "fast": { + "description": "Enable run of fast linters.", + "type": "boolean", + "default": false + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "List of regular expressions of issue texts to exclude.\nBut independently from this option we use default exclude patterns. Their usage can be controlled through `exclude-use-default`.", + "type": "array", + "items": { + "type": "string" + } + }, + "exclude-rules": { + "description": "Exclude configuration per-path, per-linter, per-text and per-source", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "exclude-use-default": { + "description": "Independently from option `exclude` we use default exclude patterns. This behavior can be disabled by this option.", + "type": "boolean", + "default": true + }, + "exclude-case-sensitive": { + "description": "If set to true, exclude and exclude-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "exclude-generated": { + "description": "Mode of the generated files analysis.", + "enum": ["lax", "strict", "disable"], + "default": "lax" + }, + "exclude-dirs": { + "description": "Which directories to exclude: issues from them won't be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. The regexp is applied on the full path.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": ["generated.*"] + }, + "default": [], + "examples": [["src/external_libs", "autogenerated_by_my_lib"]] + }, + "exclude-dirs-use-default": { + "description": "Enable exclusion of directories \"vendor\", \"third_party\", \"testdata\", \"examples\", \"Godeps\", and \"builtin\".", + "type": "boolean", + "default": true + }, + "exclude-files": { + "description": "Which files to exclude: they will be analyzed, but issues from them will not be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. There is no need to include all autogenerated files, we confidently recognize them. If that is not the case, please let us know.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": [".*\\.my\\.go$"] + }, + "default": [], + "examples": [[".*\\.my\\.go$", "lib/bad.go"]] + }, + "include": { + "description": "The list of ids of default excludes to include or disable.", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-severity": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "case-sensitive": { + "description": "If set to true, severity-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default-severity"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.63.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.63.jsonschema.json new file mode 100644 index 00000000..c54dd690 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.63.jsonschema.json @@ -0,0 +1,4082 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range", + "range-val-address", + "range-val-in-closure", + "receiver-naming", + "redefines-builtin-id", + "redundant-import-alias", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque" + ] + }, + "tagliatelle-cases": { + "enum": [ + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "linters": { + "$comment": "anyOf with enum is used to allow auto completion of non-custom linters", + "description": "Linters usable.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exportloopref", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funlen", + "gci", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "gofmt", + "gofumpt", + "goheader", + "goimports", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "tenv", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "default": "stdout", + "anyOf": [ + { + "enum": [ "stdout", "stderr" ] + }, + { + "type": "string" + } + ] + }, + "format": { + "default": "colored-line-number", + "enum": [ + "colored-line-number", + "line-number", + "json", + "colored-tab", + "tab", + "html", + "checkstyle", + "code-climate", + "junit-xml", + "junit-xml-extended", + "github-actions", + "teamcity", + "sarif" + ] + } + }, + "required": ["format"] + } + }, + "print-issued-lines": { + "description": "Print lines of code with issue.", + "type": "boolean", + "default": true + }, + "print-linter-name": { + "description": "Print linter name in the end of issue text.", + "type": "boolean", + "default": true + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": false + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + }, + "sort-results": { + "description": "Sort results by: filepath, line and column.", + "type": "boolean", + "default": true + } + } + }, + "linters-settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalint": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + }, + "ignore-test": { + "description": "Ignore *_test.go files.", + "type": "boolean", + "default": false + } + } + }, + "bidichk": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclop": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-tests": { + "description": "Should the linter execute on test files as well", + "type": "boolean", + "default": false + }, + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorder": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsled": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "dupl": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjson": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustive": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "check-generated": { + "description": "Check switch statements in generated files", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustruct": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "forbidigo": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "examples": ["^print(ln)?$"], + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "p": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + ] + } + } + } + }, + "funlen": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": false + } + } + }, + "gci": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "skip-generated": { + "description": "Skip generated files.", + "type": "boolean", + "default": true + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtype": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognit": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconst": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-strings": { + "description": "Exclude strings matching the given regular expression", + "type": "string" + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + } + } + }, + "gocritic": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocyclo": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godot": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godox": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmt": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "interfacebloat": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumpt": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheader": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimports": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a comma-separated list of prefixes.", + "type": "string", + "examples": ["github.com/org/project"] + } + } + }, + "gomoddirectives": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local_replace_directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosimple": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "gosec": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "exclude-generated": { + "description": "Exclude generated files", + "type": "boolean", + "default": false + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitan": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govet": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouper": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "iface": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importas": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturn": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lll": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidx": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezero": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspell": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-words": { + "description": "List of words to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttag": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedret": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestif": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnil": { + "type": "object", + "additionalProperties": false, + "properties": { + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturn": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mnd": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassign": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturns": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltest": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprint": { + "type": "object", + "additionalProperties": false, + "properties": { + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + } + } + }, + "prealloc": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclared": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "Comma-separated list of predeclared identifiers to not report on.", + "type": "string" + }, + "q": { + "description": "Include method names and field names (i.e., qualified names) in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetter": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "revive": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "ignore-generated-header": { + "type": "boolean" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "stylecheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + }, + "default": [ + "all", + "-ST1000", + "-ST1003", + "-ST1016", + "-ST1020", + "-ST1021", + "-ST1022" + ] + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalign": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelle": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "tenv": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "description": "The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.", + "type": "boolean", + "default": false + } + } + }, + "testifylint": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackage": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelper": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvars": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetesting": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": true + }, + "context-todo": { + "type": "boolean", + "default": true + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": false + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvert": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelen": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespace": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigRegexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignorePackageGlobs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreInterfaceRegexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wsl": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvar": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "custom": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "enable-all": { + "description": "Whether to enable all linters. You can re-disable them with `disable` explicitly.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Whether to disable all linters. You can re-enable them with `enable` explicitly.", + "type": "boolean", + "default": false + }, + "presets": { + "description": "Allow to use different presets of linters", + "type": "array", + "items": { + "enum": [ + "bugs", + "comment", + "complexity", + "error", + "format", + "import", + "metalinter", + "module", + "performance", + "sql", + "style", + "test", + "unused" + ] + } + }, + "fast": { + "description": "Enable run of fast linters.", + "type": "boolean", + "default": false + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "List of regular expressions of issue texts to exclude.\nBut independently from this option we use default exclude patterns. Their usage can be controlled through `exclude-use-default`.", + "type": "array", + "items": { + "type": "string" + } + }, + "exclude-rules": { + "description": "Exclude configuration per-path, per-linter, per-text and per-source", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "exclude-use-default": { + "description": "Independently from option `exclude` we use default exclude patterns. This behavior can be disabled by this option.", + "type": "boolean", + "default": true + }, + "exclude-case-sensitive": { + "description": "If set to true, exclude and exclude-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "exclude-generated": { + "description": "Mode of the generated files analysis.", + "enum": ["lax", "strict", "disable"], + "default": "lax" + }, + "exclude-dirs": { + "description": "Which directories to exclude: issues from them won't be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. The regexp is applied on the full path.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": ["generated.*"] + }, + "default": [], + "examples": [["src/external_libs", "autogenerated_by_my_lib"]] + }, + "exclude-dirs-use-default": { + "description": "Enable exclusion of directories \"vendor\", \"third_party\", \"testdata\", \"examples\", \"Godeps\", and \"builtin\".", + "type": "boolean", + "default": true + }, + "exclude-files": { + "description": "Which files to exclude: they will be analyzed, but issues from them will not be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. There is no need to include all autogenerated files, we confidently recognize them. If that is not the case, please let us know.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": [".*\\.my\\.go$"] + }, + "default": [], + "examples": [[".*\\.my\\.go$", "lib/bad.go"]] + }, + "include": { + "description": "The list of ids of default excludes to include or disable.", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-severity": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "case-sensitive": { + "description": "If set to true, severity-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default-severity"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.64.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.64.jsonschema.json new file mode 100644 index 00000000..b265c1a8 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.64.jsonschema.json @@ -0,0 +1,4224 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "linters": { + "$comment": "anyOf with enum is used to allow auto completion of non-custom linters", + "description": "Linters usable.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funlen", + "gci", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "gofmt", + "gofumpt", + "goheader", + "goimports", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "tenv", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "default": "stdout", + "anyOf": [ + { + "enum": [ "stdout", "stderr" ] + }, + { + "type": "string" + } + ] + }, + "format": { + "default": "colored-line-number", + "enum": [ + "colored-line-number", + "line-number", + "json", + "colored-tab", + "tab", + "html", + "checkstyle", + "code-climate", + "junit-xml", + "junit-xml-extended", + "github-actions", + "teamcity", + "sarif" + ] + } + }, + "required": ["format"] + } + }, + "print-issued-lines": { + "description": "Print lines of code with issue.", + "type": "boolean", + "default": true + }, + "print-linter-name": { + "description": "Print linter name in the end of issue text.", + "type": "boolean", + "default": true + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": false + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + }, + "sort-results": { + "description": "Sort results by: filepath, line and column.", + "type": "boolean", + "default": true + } + } + }, + "linters-settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalint": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + }, + "ignore-test": { + "description": "Ignore *_test.go files.", + "type": "boolean", + "default": false + } + } + }, + "bidichk": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclop": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-tests": { + "description": "Should the linter execute on test files as well", + "type": "boolean", + "default": false + }, + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorder": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsled": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "dupl": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjson": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustive": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "check-generated": { + "description": "Check switch statements in generated files", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustruct": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "fatcontext": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigo": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "examples": ["^print(ln)?$"], + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "p": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + ] + } + } + } + }, + "funlen": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": false + } + } + }, + "gci": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "skip-generated": { + "description": "Skip generated files.", + "type": "boolean", + "default": true + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtype": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognit": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconst": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-strings": { + "description": "Exclude strings matching the given regular expression", + "type": "string" + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + } + } + }, + "gocritic": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocyclo": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godot": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godox": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmt": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "interfacebloat": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumpt": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheader": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimports": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a comma-separated list of prefixes.", + "type": "string", + "examples": ["github.com/org/project"] + } + } + }, + "gomoddirectives": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local_replace_directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosimple": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "gosec": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "exclude-generated": { + "description": "Exclude generated files", + "type": "boolean", + "default": false + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitan": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govet": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouper": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "iface": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importas": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturn": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lll": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidx": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezero": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspell": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-words": { + "description": "List of words to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttag": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedret": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestif": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnil": { + "type": "object", + "additionalProperties": false, + "properties": { + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturn": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mnd": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassign": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturns": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltest": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprint": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + } + } + }, + "prealloc": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclared": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "Comma-separated list of predeclared identifiers to not report on.", + "type": "string" + }, + "q": { + "description": "Include method names and field names (i.e., qualified names) in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetter": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "revive": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "ignore-generated-header": { + "type": "boolean" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "stylecheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + }, + "default": [ + "all", + "-ST1000", + "-ST1003", + "-ST1016", + "-ST1020", + "-ST1021", + "-ST1022" + ] + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalign": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelle": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "tenv": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "description": "The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.", + "type": "boolean", + "default": false + } + } + }, + "testifylint": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackage": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelper": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvars": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetesting": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": true + }, + "context-todo": { + "type": "boolean", + "default": true + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvert": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelen": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespace": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigRegexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignorePackageGlobs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreInterfaceRegexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wsl": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvar": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "custom": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "enable-all": { + "description": "Whether to enable all linters. You can re-disable them with `disable` explicitly.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Whether to disable all linters. You can re-enable them with `enable` explicitly.", + "type": "boolean", + "default": false + }, + "presets": { + "description": "Allow to use different presets of linters", + "type": "array", + "items": { + "enum": [ + "bugs", + "comment", + "complexity", + "error", + "format", + "import", + "metalinter", + "module", + "performance", + "sql", + "style", + "test", + "unused" + ] + } + }, + "fast": { + "description": "Enable run of fast linters.", + "type": "boolean", + "default": false + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "lax" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "default": { + "type": "array", + "items": { + "enum": [ + "comments", + "stdErrorHandling", + "commonFalsePositives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "List of regular expressions of issue texts to exclude.\nBut independently from this option we use default exclude patterns. Their usage can be controlled through `exclude-use-default`.", + "type": "array", + "items": { + "type": "string" + } + }, + "exclude-rules": { + "description": "Exclude configuration per-path, per-linter, per-text and per-source", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "exclude-use-default": { + "description": "Independently from option `exclude` we use default exclude patterns. This behavior can be disabled by this option.", + "type": "boolean", + "default": true + }, + "exclude-case-sensitive": { + "description": "If set to true, exclude and exclude-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "exclude-generated": { + "description": "Mode of the generated files analysis.", + "enum": ["lax", "strict", "disable"], + "default": "lax" + }, + "exclude-dirs": { + "description": "Which directories to exclude: issues from them won't be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. The regexp is applied on the full path.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": ["generated.*"] + }, + "default": [], + "examples": [["src/external_libs", "autogenerated_by_my_lib"]] + }, + "exclude-dirs-use-default": { + "description": "Enable exclusion of directories \"vendor\", \"third_party\", \"testdata\", \"examples\", \"Godeps\", and \"builtin\".", + "type": "boolean", + "default": true + }, + "exclude-files": { + "description": "Which files to exclude: they will be analyzed, but issues from them will not be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. There is no need to include all autogenerated files, we confidently recognize them. If that is not the case, please let us know.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": [".*\\.my\\.go$"] + }, + "default": [], + "examples": [[".*\\.my\\.go$", "lib/bad.go"]] + }, + "include": { + "description": "The list of ids of default excludes to include or disable.", + "type": "array", + "items": { + "enum": [ + "EXC0001", + "EXC0002", + "EXC0003", + "EXC0004", + "EXC0005", + "EXC0006", + "EXC0007", + "EXC0008", + "EXC0009", + "EXC0010", + "EXC0011", + "EXC0012", + "EXC0013", + "EXC0014", + "EXC0015" + ] + }, + "default": [] + }, + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-severity": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "case-sensitive": { + "description": "If set to true, severity-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default-severity"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.jsonschema.json new file mode 100644 index 00000000..b265c1a8 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v1.jsonschema.json @@ -0,0 +1,4224 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "linters": { + "$comment": "anyOf with enum is used to allow auto completion of non-custom linters", + "description": "Linters usable.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funlen", + "gci", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "gofmt", + "gofumpt", + "goheader", + "goimports", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "tenv", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "default": "stdout", + "anyOf": [ + { + "enum": [ "stdout", "stderr" ] + }, + { + "type": "string" + } + ] + }, + "format": { + "default": "colored-line-number", + "enum": [ + "colored-line-number", + "line-number", + "json", + "colored-tab", + "tab", + "html", + "checkstyle", + "code-climate", + "junit-xml", + "junit-xml-extended", + "github-actions", + "teamcity", + "sarif" + ] + } + }, + "required": ["format"] + } + }, + "print-issued-lines": { + "description": "Print lines of code with issue.", + "type": "boolean", + "default": true + }, + "print-linter-name": { + "description": "Print linter name in the end of issue text.", + "type": "boolean", + "default": true + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": false + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + }, + "sort-results": { + "description": "Sort results by: filepath, line and column.", + "type": "boolean", + "default": true + } + } + }, + "linters-settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalint": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + }, + "ignore-test": { + "description": "Ignore *_test.go files.", + "type": "boolean", + "default": false + } + } + }, + "bidichk": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclop": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-tests": { + "description": "Should the linter execute on test files as well", + "type": "boolean", + "default": false + }, + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorder": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsled": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "dupl": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjson": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustive": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "check-generated": { + "description": "Check switch statements in generated files", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustruct": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "fatcontext": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigo": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "examples": ["^print(ln)?$"], + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "p": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + ] + } + } + } + }, + "funlen": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": false + } + } + }, + "gci": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "skip-generated": { + "description": "Skip generated files.", + "type": "boolean", + "default": true + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtype": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognit": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconst": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-strings": { + "description": "Exclude strings matching the given regular expression", + "type": "string" + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + } + } + }, + "gocritic": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocyclo": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godot": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godox": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmt": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "interfacebloat": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumpt": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheader": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimports": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a comma-separated list of prefixes.", + "type": "string", + "examples": ["github.com/org/project"] + } + } + }, + "gomoddirectives": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local_replace_directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosimple": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "gosec": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "exclude-generated": { + "description": "Exclude generated files", + "type": "boolean", + "default": false + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitan": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-tests": { + "description": "Ignore test files.", + "type": "boolean", + "default": false + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govet": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouper": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "iface": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importas": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturn": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lll": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidx": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezero": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspell": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-words": { + "description": "List of words to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttag": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedret": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestif": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnil": { + "type": "object", + "additionalProperties": false, + "properties": { + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturn": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mnd": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlint": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassign": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturns": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltest": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprint": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + } + } + }, + "prealloc": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclared": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "Comma-separated list of predeclared identifiers to not report on.", + "type": "string" + }, + "q": { + "description": "Include method names and field names (i.e., qualified names) in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinter": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetter": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "revive": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "ignore-generated-header": { + "type": "boolean" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + } + } + } + }, + "stylecheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "enum": ["all"] + }, + { + "type": "string" + } + ] + }, + "default": [ + "all", + "-ST1000", + "-ST1003", + "-ST1016", + "-ST1020", + "-ST1021", + "-ST1022" + ] + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalign": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelle": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "tenv": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "description": "The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.", + "type": "boolean", + "default": false + } + } + }, + "testifylint": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackage": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelper": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvars": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetesting": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": true + }, + "context-todo": { + "type": "boolean", + "default": true + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvert": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparam": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelen": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespace": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheck": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreSigRegexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignorePackageGlobs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignoreInterfaceRegexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wsl": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvar": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "custom": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "enable-all": { + "description": "Whether to enable all linters. You can re-disable them with `disable` explicitly.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Whether to disable all linters. You can re-enable them with `enable` explicitly.", + "type": "boolean", + "default": false + }, + "presets": { + "description": "Allow to use different presets of linters", + "type": "array", + "items": { + "enum": [ + "bugs", + "comment", + "complexity", + "error", + "format", + "import", + "metalinter", + "module", + "performance", + "sql", + "style", + "test", + "unused" + ] + } + }, + "fast": { + "description": "Enable run of fast linters.", + "type": "boolean", + "default": false + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "lax" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "default": { + "type": "array", + "items": { + "enum": [ + "comments", + "stdErrorHandling", + "commonFalsePositives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "List of regular expressions of issue texts to exclude.\nBut independently from this option we use default exclude patterns. Their usage can be controlled through `exclude-use-default`.", + "type": "array", + "items": { + "type": "string" + } + }, + "exclude-rules": { + "description": "Exclude configuration per-path, per-linter, per-text and per-source", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "exclude-use-default": { + "description": "Independently from option `exclude` we use default exclude patterns. This behavior can be disabled by this option.", + "type": "boolean", + "default": true + }, + "exclude-case-sensitive": { + "description": "If set to true, exclude and exclude-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "exclude-generated": { + "description": "Mode of the generated files analysis.", + "enum": ["lax", "strict", "disable"], + "default": "lax" + }, + "exclude-dirs": { + "description": "Which directories to exclude: issues from them won't be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. The regexp is applied on the full path.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": ["generated.*"] + }, + "default": [], + "examples": [["src/external_libs", "autogenerated_by_my_lib"]] + }, + "exclude-dirs-use-default": { + "description": "Enable exclusion of directories \"vendor\", \"third_party\", \"testdata\", \"examples\", \"Godeps\", and \"builtin\".", + "type": "boolean", + "default": true + }, + "exclude-files": { + "description": "Which files to exclude: they will be analyzed, but issues from them will not be reported.", + "type": "array", + "items": { + "description": "You can use regexp here. There is no need to include all autogenerated files, we confidently recognize them. If that is not the case, please let us know.\n\"/\" will be replaced by current OS file path separator to properly work on Windows.", + "type": "string", + "examples": [".*\\.my\\.go$"] + }, + "default": [], + "examples": [[".*\\.my\\.go$", "lib/bad.go"]] + }, + "include": { + "description": "The list of ids of default excludes to include or disable.", + "type": "array", + "items": { + "enum": [ + "EXC0001", + "EXC0002", + "EXC0003", + "EXC0004", + "EXC0005", + "EXC0006", + "EXC0007", + "EXC0008", + "EXC0009", + "EXC0010", + "EXC0011", + "EXC0012", + "EXC0013", + "EXC0014", + "EXC0015" + ] + }, + "default": [] + }, + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-severity": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "case-sensitive": { + "description": "If set to true, severity-rules regular expressions become case sensitive.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linters" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default-severity"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.0.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.0.jsonschema.json new file mode 100644 index 00000000..437943b5 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.0.jsonschema.json @@ -0,0 +1,4738 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-strings": { + "description": "Exclude strings matching the given regular expression", + "type": "string" + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": true + }, + "context-todo": { + "type": "boolean", + "default": true + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.1.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.1.jsonschema.json new file mode 100644 index 00000000..0f2ef5fc --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.1.jsonschema.json @@ -0,0 +1,4790 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": true + }, + "context-todo": { + "type": "boolean", + "default": true + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.10.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.10.jsonschema.json new file mode 100644 index 00000000..7fa4a12d --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.10.jsonschema.json @@ -0,0 +1,5285 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupOption", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr", + "zeroByteRepeat" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "godoclint-rules": { + "enum": [ + "pkg-doc", + "single-pkg-doc", + "require-pkg-doc", + "start-with-name", + "require-doc", + "deprecated", + "max-len", + "no-unused-link", + "require-stdlib-doclink" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G114", + "G115", + "G116", + "G117", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602", + "G701", + "G702", + "G703", + "G704", + "G705", + "G706" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "epoch-naming", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "forbidden-call-in-wg-go", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "identical-ifelseif-branches", + "identical-ifelseif-conditions", + "identical-switch-branches", + "identical-switch-conditions", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "inefficient-map-lookup", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "package-directory-mismatch", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-if", + "unnecessary-stmt", + "unreachable-code", + "unsecure-url-scheme", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "use-slices-sort", + "use-waitgroup-go", + "useless-break", + "useless-fallthrough", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "modernize-analyzers": { + "enum": [ + "any", + "fmtappendf", + "forvar", + "mapsloop", + "minmax", + "newexpr", + "omitzero", + "plusbuild", + "rangeint", + "reflecttypefor", + "slicescontains", + "slicessort", + "stditerators", + "stringscut", + "stringscutprefix", + "stringsseq", + "stringsbuilder", + "testingcontext", + "unsafefuncs", + "waitgroup" + ] + }, + "wsl-checks": { + "enum": [ + "assign", + "branch", + "decl", + "defer", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "range", + "return", + "select", + "send", + "switch", + "type-switch", + "append", + "assign-exclusive", + "assign-expr", + "err", + "leading-whitespace", + "trailing-whitespace", + "after-block" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godoclint", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "iotamixing", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "modernize", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + }, + "comments-only": { + "description": "Checks only comments, skip strings.", + "type": "boolean", + "default": false + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "empty-line": { + "description": "Checks that there is an empty space between the embedded fields and regular fields.", + "type": "boolean", + "default": false + }, + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + }, + "force-tonot": { + "description": "Force using `ToNot`, `ShouldNot` instead of `To(Not())`.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godoclintSettings": { + "type": "object", + "properties": { + "default": { + "type": "string", + "enum": ["all", "basic", "none"], + "default": "basic", + "description": "Default set of rules to enable." + }, + "enable": { + "description": "List of rules to enable in addition to the default set.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "disable": { + "description": "List of rules to disable.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "options": { + "type": "object", + "description": "A map for setting individual rule options.", + "properties": { + "max-len": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "description": "Maximum line length for godocs, not including the `//`, `/*` or `*/` tokens.", + "default": 77 + } + } + }, + "require-doc": { + "type": "object", + "properties": { + "ignore-exported": { + "type": "boolean", + "description": "Ignore exported (public) symbols when applying the `require-doc` rule.", + "default": false + }, + "ignore-unexported": { + "type": "boolean", + "description": "Ignore unexported (private) symbols when applying the `require-doc` rule.", + "default": true + } + } + }, + "start-with-name": { + "type": "object", + "properties": { + "include-unexported": { + "type": "boolean", + "description": "Include unexported symbols when applying the `start-with-name` rule.", + "default": false + } + } + } + } + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + }, + "check-module-path": { + "description": "Check the validity of the module path.", + "type": "boolean", + "default": false + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ineffassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-escaping-errors": { + "description": "Check escaping variables of type error, may cause false positives.", + "type": "boolean", + "default": false + } + } + }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "modernizeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "description": "List of analyzers to disable.", + "type": "array", + "items": { + "$ref": "#/definitions/modernize-analyzers" + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + }, + "concat-loop": { + "description": "Enable/disable optimization of concat loop.", + "type": "boolean", + "default": true + }, + "loop-other-ops": { + "description": "Optimization of `concat-loop` even with other operations.", + "type": "boolean", + "default": false + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "enable-default-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unqueryvetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-sql-builders": { + "description": "Enable SQL builder checking.", + "type": "boolean", + "default": true + }, + "check-aliased-wildcard": { + "description": "Enable aliased wildcard detection like SELECT t.*.", + "type": "boolean", + "default": true + }, + "check-string-concat": { + "description": "Enable string concatenation analysis.", + "type": "boolean", + "default": true + }, + "check-format-strings": { + "description": "Enable format string analysis like fmt.Sprintf.", + "type": "boolean", + "default": true + }, + "check-string-builder": { + "description": "Enable strings.Builder analysis.", + "type": "boolean", + "default": true + }, + "check-subqueries": { + "description": "Enable subquery analysis.", + "type": "boolean", + "default": true + }, + "check-n1": { + "type": "boolean", + "default": false + }, + "check-sql-injection": { + "type": "boolean", + "default": false + }, + "check-tx-leaks": { + "type": "boolean", + "default": false + }, + "allowed-patterns": { + "description": "Regex patterns for acceptable SELECT * usage.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "Allow is a list of SQL patterns to allow (whitelist).", + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Functions to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "sql-builders": { + "type": "object", + "additionalProperties": false, + "properties": { + "squirrel": { + "type": "boolean", + "default": true + }, + "gorm": { + "type": "boolean", + "default": true + }, + "sqlx": { + "type": "boolean", + "default": true + }, + "ent": { + "type": "boolean", + "default": true + }, + "pgx": { + "type": "boolean", + "default": true + }, + "bun": { + "type": "boolean", + "default": true + }, + "sqlboiler": { + "type": "boolean", + "default": true + }, + "jet": { + "type": "boolean", + "default": true + } + } + }, + "custom-rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "pattern": { + "type": "string" + }, + "patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "when": { + "type": "string" + }, + "message": { + "type": "string" + }, + "action": { + "type": "string" + } + } + } + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "enable-build-vcs": { + "type": "boolean", + "default": false + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godoclint": { + "$ref": "#/definitions/settings/definitions/godoclintSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ineffassign": { + "$ref": "#/definitions/settings/definitions/ineffassignSettings" + }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "modernize": { + "$ref": "#/definitions/settings/definitions/modernizeSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unqueryvet": { + "$ref": "#/definitions/settings/definitions/unqueryvetSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Apply the fixes detected by the linters and formatters (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.11.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.11.jsonschema.json new file mode 100644 index 00000000..38b3e77a --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.11.jsonschema.json @@ -0,0 +1,5295 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupOption", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr", + "zeroByteRepeat" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "godoclint-rules": { + "enum": [ + "pkg-doc", + "single-pkg-doc", + "require-pkg-doc", + "start-with-name", + "require-doc", + "deprecated", + "max-len", + "no-unused-link", + "require-stdlib-doclink" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G116", + "G117", + "G118", + "G119", + "G120", + "G121", + "G122", + "G123", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G408", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602", + "G701", + "G702", + "G703", + "G704", + "G705", + "G706", + "G707" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "epoch-naming", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "forbidden-call-in-wg-go", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "identical-ifelseif-branches", + "identical-ifelseif-conditions", + "identical-switch-branches", + "identical-switch-conditions", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "inefficient-map-lookup", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "package-naming", + "package-directory-mismatch", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-if", + "unnecessary-stmt", + "unreachable-code", + "unsecure-url-scheme", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "use-slices-sort", + "use-waitgroup-go", + "useless-break", + "useless-fallthrough", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "modernize-analyzers": { + "enum": [ + "any", + "fmtappendf", + "forvar", + "mapsloop", + "minmax", + "newexpr", + "omitzero", + "plusbuild", + "rangeint", + "reflecttypefor", + "slicescontains", + "slicessort", + "stditerators", + "stringscut", + "stringscutprefix", + "stringsseq", + "stringsbuilder", + "testingcontext", + "unsafefuncs", + "waitgroup" + ] + }, + "wsl-checks": { + "enum": [ + "assign", + "branch", + "decl", + "defer", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "range", + "return", + "select", + "send", + "switch", + "type-switch", + "append", + "assign-exclusive", + "assign-expr", + "err", + "leading-whitespace", + "trailing-whitespace", + "after-block" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godoclint", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "iotamixing", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "modernize", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + }, + "comments-only": { + "description": "Checks only comments, skip strings.", + "type": "boolean", + "default": false + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "empty-line": { + "description": "Checks that there is an empty space between the embedded fields and regular fields.", + "type": "boolean", + "default": false + }, + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + }, + "force-tonot": { + "description": "Force using `ToNot`, `ShouldNot` instead of `To(Not())`.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godoclintSettings": { + "type": "object", + "properties": { + "default": { + "type": "string", + "enum": ["all", "basic", "none"], + "default": "basic", + "description": "Default set of rules to enable." + }, + "enable": { + "description": "List of rules to enable in addition to the default set.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "disable": { + "description": "List of rules to disable.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "options": { + "type": "object", + "description": "A map for setting individual rule options.", + "properties": { + "max-len": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "description": "Maximum line length for godocs, not including the `//`, `/*` or `*/` tokens.", + "default": 77 + } + } + }, + "require-doc": { + "type": "object", + "properties": { + "ignore-exported": { + "type": "boolean", + "description": "Ignore exported (public) symbols when applying the `require-doc` rule.", + "default": false + }, + "ignore-unexported": { + "type": "boolean", + "description": "Ignore unexported (private) symbols when applying the `require-doc` rule.", + "default": true + } + } + }, + "start-with-name": { + "type": "object", + "properties": { + "include-unexported": { + "type": "boolean", + "description": "Include unexported symbols when applying the `start-with-name` rule.", + "default": false + } + } + } + } + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + }, + "check-module-path": { + "description": "Check the validity of the module path.", + "type": "boolean", + "default": false + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ineffassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-escaping-errors": { + "description": "Check escaping variables of type error, may cause false positives.", + "type": "boolean", + "default": false + } + } + }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "modernizeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "description": "List of analyzers to disable.", + "type": "array", + "items": { + "$ref": "#/definitions/modernize-analyzers" + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + }, + "concat-loop": { + "description": "Enable/disable optimization of concat loop.", + "type": "boolean", + "default": true + }, + "loop-other-ops": { + "description": "Optimization of `concat-loop` even with other operations.", + "type": "boolean", + "default": false + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "enable-default-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unqueryvetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-sql-builders": { + "description": "Enable SQL builder checking.", + "type": "boolean", + "default": true + }, + "check-aliased-wildcard": { + "description": "Enable aliased wildcard detection like SELECT t.*.", + "type": "boolean", + "default": true + }, + "check-string-concat": { + "description": "Enable string concatenation analysis.", + "type": "boolean", + "default": true + }, + "check-format-strings": { + "description": "Enable format string analysis like fmt.Sprintf.", + "type": "boolean", + "default": true + }, + "check-string-builder": { + "description": "Enable strings.Builder analysis.", + "type": "boolean", + "default": true + }, + "check-subqueries": { + "description": "Enable subquery analysis.", + "type": "boolean", + "default": true + }, + "check-n1": { + "type": "boolean", + "default": false + }, + "check-sql-injection": { + "type": "boolean", + "default": false + }, + "check-tx-leaks": { + "type": "boolean", + "default": false + }, + "allowed-patterns": { + "description": "Regex patterns for acceptable SELECT * usage.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "Allow is a list of SQL patterns to allow (whitelist).", + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Functions to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "sql-builders": { + "type": "object", + "additionalProperties": false, + "properties": { + "squirrel": { + "type": "boolean", + "default": true + }, + "gorm": { + "type": "boolean", + "default": true + }, + "sqlx": { + "type": "boolean", + "default": true + }, + "ent": { + "type": "boolean", + "default": true + }, + "pgx": { + "type": "boolean", + "default": true + }, + "bun": { + "type": "boolean", + "default": true + }, + "sqlboiler": { + "type": "boolean", + "default": true + }, + "jet": { + "type": "boolean", + "default": true + } + } + }, + "custom-rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "pattern": { + "type": "string" + }, + "patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "when": { + "type": "string" + }, + "message": { + "type": "string" + }, + "action": { + "type": "string" + } + } + } + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "enable-build-vcs": { + "type": "boolean", + "default": false + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godoclint": { + "$ref": "#/definitions/settings/definitions/godoclintSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ineffassign": { + "$ref": "#/definitions/settings/definitions/ineffassignSettings" + }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "modernize": { + "$ref": "#/definitions/settings/definitions/modernizeSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unqueryvet": { + "$ref": "#/definitions/settings/definitions/unqueryvetSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Apply the fixes detected by the linters and formatters (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.12.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.12.jsonschema.json new file mode 100644 index 00000000..0ac9d577 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.12.jsonschema.json @@ -0,0 +1,5438 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupOption", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr", + "zeroByteRepeat" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "godoclint-rules": { + "enum": [ + "pkg-doc", + "single-pkg-doc", + "require-pkg-doc", + "start-with-name", + "require-doc", + "deprecated", + "max-len", + "no-unused-link", + "require-stdlib-doclink" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G113", + "G114", + "G115", + "G116", + "G117", + "G118", + "G119", + "G120", + "G121", + "G122", + "G123", + "G124", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G408", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602", + "G701", + "G702", + "G703", + "G704", + "G705", + "G706", + "G707", + "G708", + "G709", + "G710" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "inline", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "epoch-naming", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "forbidden-call-in-wg-go", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "identical-ifelseif-branches", + "identical-ifelseif-conditions", + "identical-switch-branches", + "identical-switch-conditions", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "inefficient-map-lookup", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "package-naming", + "package-directory-mismatch", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-if", + "unnecessary-stmt", + "unreachable-code", + "unsecure-url-scheme", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "use-slices-sort", + "use-waitgroup-go", + "useless-break", + "useless-fallthrough", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "modernize-analyzers": { + "enum": [ + "any", + "fmtappendf", + "forvar", + "mapsloop", + "minmax", + "newexpr", + "omitzero", + "plusbuild", + "rangeint", + "reflecttypefor", + "slicescontains", + "slicessort", + "stditerators", + "stringscut", + "stringscutprefix", + "stringsseq", + "stringsbuilder", + "testingcontext", + "unsafefuncs", + "waitgroup" + ] + }, + "wsl-checks": { + "enum": [ + "after-block", + "after-decl", + "after-defer", + "after-expr", + "after-go", + "append", + "assign-exclusive", + "assign-expr", + "assign", + "branch", + "cuddle-group", + "decl", + "defer", + "err", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "leading-whitespace", + "range", + "return", + "select", + "send", + "switch", + "trailing-whitespace", + "type-switch" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "clickhouselint", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godoclint", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "gomodguard_v2", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "iotamixing", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "modernize", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + }, + "comments-only": { + "description": "Checks only comments, skip strings.", + "type": "boolean", + "default": false + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "bodycloseSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-consumption": { + "description": "Check that the response body is consumed.", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "empty-line": { + "description": "Checks that there is an empty space between the embedded fields and regular fields.", + "type": "boolean", + "default": false + }, + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + }, + "function": { + "description": "Checks that exported functions are placed before unexported functions.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + }, + "force-tonot": { + "description": "Force using `ToNot`, `ShouldNot` instead of `To(Not())`.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + }, + "ignore-tests": { + "description": "Ignore strings from test files", + "type": "boolean", + "default": false + }, + "ignore-functions": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godoclintSettings": { + "type": "object", + "properties": { + "default": { + "type": "string", + "enum": ["all", "basic", "none"], + "default": "basic", + "description": "Default set of rules to enable." + }, + "enable": { + "description": "List of rules to enable in addition to the default set.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "disable": { + "description": "List of rules to disable.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "options": { + "type": "object", + "description": "A map for setting individual rule options.", + "properties": { + "max-len": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "description": "Maximum line length for godocs, not including the `//`, `/*` or `*/` tokens.", + "default": 77 + } + } + }, + "require-doc": { + "type": "object", + "properties": { + "ignore-exported": { + "type": "boolean", + "description": "Ignore exported (public) symbols when applying the `require-doc` rule.", + "default": false + }, + "ignore-unexported": { + "type": "boolean", + "description": "Ignore unexported (private) symbols when applying the `require-doc` rule.", + "default": true + } + } + }, + "start-with-name": { + "type": "object", + "properties": { + "include-unexported": { + "type": "boolean", + "description": "Include unexported symbols when applying the `start-with-name` rule.", + "default": false + } + } + } + } + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + }, + "check-module-path": { + "description": "Check the validity of the module path.", + "type": "boolean", + "default": false + } + } + }, + "gomodguardv2Settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-replace-directives": { + "type": "boolean" + }, + "allowed": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["module"], + "properties": { + "module": { + "type": "string" + }, + "version": { + "type": "string" + }, + "match-type": { + "enum": ["", "exact", "prefix", "regex"], + "default": "exact" + } + } + } + }, + "blocked": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["module"], + "properties": { + "module": { + "type": "string" + }, + "version": { + "type": "string" + }, + "match-type": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "recommendations": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ineffassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-escaping-errors": { + "description": "Check escaping variables of type error, may cause false positives.", + "type": "boolean", + "default": false + } + } + }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "modernizeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "description": "List of analyzers to disable.", + "type": "array", + "items": { + "$ref": "#/definitions/modernize-analyzers" + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + }, + "check-cleanup": { + "description": "Check that defer is not used with t.Parallel (use t.Cleanup instead).", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + }, + "concat-loop": { + "description": "Enable/disable optimization of concat loop.", + "type": "boolean", + "default": true + }, + "loop-other-ops": { + "description": "Optimization of `concat-loop` even with other operations.", + "type": "boolean", + "default": false + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "enable-default-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-global": { + "description": "Report the use of global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "context": { + "description": "Report the use of functions without a context.Context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Report dynamic log messages, such as those that are built with fmt.Sprintf.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Report log messages that do not match a particular style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "no-mixed-args": { + "description": "Report the use of both key-value pairs and attributes within a single function call.", + "type": "boolean", + "default": true + }, + "kv-only": { + "description": "Report any use of attributes as function call arguments.", + "type": "boolean", + "default": false + }, + "attr-only": { + "description": "Report any use of key-value pairs as function call arguments.", + "type": "boolean", + "default": false + }, + "args-on-sep-lines": { + "description": "Report two or more arguments on the same line.", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Report the use of string literals as log keys.", + "type": "boolean", + "default": false + }, + "allowed-keys": { + "description": "Report the use of log keys that are not explicitly allowed.", + "type": "array", + "items": { + "type": "string" + } + }, + "forbidden-keys": { + "description": "Report the use of forbidden log keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "key-naming-case": { + "description": "Report log keys that do not match a particular naming case.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "custom-funcs": { + "description": "Analyze custom functions in addition to the standard log/slog functions.", + "type": "array", + "items": { + "$ref": "#/definitions/settings/definitions/sloglintCustomFunc" + } + } + } + }, + "sloglintCustomFunc": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "The full name of the function, including the package. If the function is a method, the receiver type must be wrapped in parentheses.", + "type": "string" + }, + "msg-pos": { + "description": "The position of the \"msg string\" argument in the function signature, starting from 0. If there is no message in the function, a negative value must be passed.", + "type": "integer" + }, + "args-pos": { + "description": "The position of the \"args ...any\" argument in the function signature, starting from 0. If there are no arguments in the function, a negative value must be passed.", + "type": "integer" + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unqueryvetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-sql-builders": { + "description": "Enable SQL builder checking.", + "type": "boolean", + "default": true + }, + "check-aliased-wildcard": { + "description": "Enable aliased wildcard detection like SELECT t.*.", + "type": "boolean", + "default": true + }, + "check-string-concat": { + "description": "Enable string concatenation analysis.", + "type": "boolean", + "default": true + }, + "check-format-strings": { + "description": "Enable format string analysis like fmt.Sprintf.", + "type": "boolean", + "default": true + }, + "check-string-builder": { + "description": "Enable strings.Builder analysis.", + "type": "boolean", + "default": true + }, + "check-subqueries": { + "description": "Enable subquery analysis.", + "type": "boolean", + "default": true + }, + "check-n1": { + "type": "boolean", + "default": false + }, + "check-sql-injection": { + "type": "boolean", + "default": false + }, + "check-tx-leaks": { + "type": "boolean", + "default": false + }, + "allowed-patterns": { + "description": "Regex patterns for acceptable SELECT * usage.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "Allow is a list of SQL patterns to allow (whitelist).", + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Functions to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "sql-builders": { + "type": "object", + "additionalProperties": false, + "properties": { + "squirrel": { + "type": "boolean", + "default": true + }, + "gorm": { + "type": "boolean", + "default": true + }, + "sqlx": { + "type": "boolean", + "default": true + }, + "ent": { + "type": "boolean", + "default": true + }, + "pgx": { + "type": "boolean", + "default": true + }, + "bun": { + "type": "boolean", + "default": true + }, + "sqlboiler": { + "type": "boolean", + "default": true + }, + "jet": { + "type": "boolean", + "default": true + } + } + }, + "custom-rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "pattern": { + "type": "string" + }, + "patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "when": { + "type": "string" + }, + "message": { + "type": "string" + }, + "action": { + "type": "string" + } + } + } + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "cuddle-max-statements": { + "type": "integer", + "default": 1 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "enable-build-vcs": { + "type": "boolean", + "default": false + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "bodyclose": { + "$ref": "#/definitions/settings/definitions/bodycloseSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godoclint": { + "$ref": "#/definitions/settings/definitions/godoclintSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gomodguard_v2": { + "$ref": "#/definitions/settings/definitions/gomodguardv2Settings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ineffassign": { + "$ref": "#/definitions/settings/definitions/ineffassignSettings" + }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "modernize": { + "$ref": "#/definitions/settings/definitions/modernizeSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unqueryvet": { + "$ref": "#/definitions/settings/definitions/unqueryvetSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Apply the fixes detected by the linters and formatters (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.2.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.2.jsonschema.json new file mode 100644 index 00000000..3e51264f --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.2.jsonschema.json @@ -0,0 +1,4901 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "wsl-checks": { + "enum": [ + "assign", + "branch", + "decl", + "defer", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "range", + "return", + "select", + "send", + "switch", + "type-switch", + "append", + "assign-exclusive", + "assign-expr", + "err", + "leading-whitespace", + "trailing-whitespace" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.3.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.3.jsonschema.json new file mode 100644 index 00000000..7c484052 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.3.jsonschema.json @@ -0,0 +1,4907 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "wsl-checks": { + "enum": [ + "assign", + "branch", + "decl", + "defer", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "range", + "return", + "select", + "send", + "switch", + "type-switch", + "append", + "assign-exclusive", + "assign-expr", + "err", + "leading-whitespace", + "trailing-whitespace" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.4.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.4.jsonschema.json new file mode 100644 index 00000000..67cd80a5 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.4.jsonschema.json @@ -0,0 +1,4929 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-stmt", + "unreachable-code", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "useless-break", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "wsl-checks": { + "enum": [ + "assign", + "branch", + "decl", + "defer", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "range", + "return", + "select", + "send", + "switch", + "type-switch", + "append", + "assign-exclusive", + "assign-expr", + "err", + "leading-whitespace", + "trailing-whitespace" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.5.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.5.jsonschema.json new file mode 100644 index 00000000..f471fd3d --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.5.jsonschema.json @@ -0,0 +1,5081 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "ioutilDeprecated", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "godoclint-rules": { + "enum": [ + "pkg-doc", + "single-pkg-doc", + "require-pkg-doc", + "start-with-name", + "require-doc", + "deprecated", + "max-len", + "no-unused-link" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "identical-ifelseif-branches", + "identical-ifelseif-conditions", + "identical-switch-branches", + "identical-switch-conditions", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "package-directory-mismatch", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-stmt", + "unreachable-code", + "unsecure-url-scheme", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "use-waitgroup-go", + "useless-break", + "useless-fallthrough", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "wsl-checks": { + "enum": [ + "assign", + "branch", + "decl", + "defer", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "range", + "return", + "select", + "send", + "switch", + "type-switch", + "append", + "assign-exclusive", + "assign-expr", + "err", + "leading-whitespace", + "trailing-whitespace" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godoclint", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "iotamixing", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "empty-line": { + "description": "Checks that there is an empty space between the embedded fields and regular fields.", + "type": "boolean", + "default": false + }, + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + }, + "force-tonot": { + "description": "Force using `ToNot`, `ShouldNot` instead of `To(Not())`.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godoclintSettings": { + "type": "object", + "properties": { + "default": { + "type": "string", + "enum": ["all", "basic", "none"], + "default": "basic", + "description": "Default set of rules to enable." + }, + "enable": { + "description": "List of rules to enable in addition to the default set.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "disable": { + "description": "List of rules to disable.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "options": { + "type": "object", + "description": "A map for setting individual rule options.", + "properties": { + "max-len": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "description": "Maximum line length for godocs, not including the `// `, or `/*` or `*/` tokens.", + "default": 77 + } + } + }, + "require-doc": { + "type": "object", + "properties": { + "ignore-exported": { + "type": "boolean", + "description": "Ignore exported (public) symbols when applying the `require-doc` rule.", + "default": false + }, + "ignore-unexported": { + "type": "boolean", + "description": "Ignore unexported (private) symbols when applying the `require-doc` rule.", + "default": true + } + } + }, + "start-with-name": { + "type": "object", + "properties": { + "include-unexported": { + "type": "boolean", + "description": "Include unexported symbols when applying the `start-with-name` rule.", + "default": false + } + } + } + } + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ineffassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-escaping-errors": { + "description": "Check escaping variables of type error, may cause false positives.", + "type": "boolean", + "default": false + } + } + }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unqueryvetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-sql-builders": { + "description": "Enable SQL builder checking.", + "type": "boolean", + "default": true + }, + "allowed-patterns": { + "description": "Regex patterns for acceptable SELECT * usage.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godoclint": { + "$ref": "#/definitions/settings/definitions/godoclintSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ineffassign": { + "$ref": "#/definitions/settings/definitions/ineffassignSettings" + }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unqueryvet": { + "$ref": "#/definitions/settings/definitions/unqueryvetSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Fix found issues (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.6.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.6.jsonschema.json new file mode 100644 index 00000000..e2f59663 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.6.jsonschema.json @@ -0,0 +1,5136 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupOption", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr", + "zeroByteRepeat" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "godoclint-rules": { + "enum": [ + "pkg-doc", + "single-pkg-doc", + "require-pkg-doc", + "start-with-name", + "require-doc", + "deprecated", + "max-len", + "no-unused-link" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "identical-ifelseif-branches", + "identical-ifelseif-conditions", + "identical-switch-branches", + "identical-switch-conditions", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "package-directory-mismatch", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-stmt", + "unreachable-code", + "unsecure-url-scheme", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "use-waitgroup-go", + "useless-break", + "useless-fallthrough", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "modernize-analyzers": { + "enum": [ + "any", + "bloop", + "fmtappendf", + "forvar", + "mapsloop", + "minmax", + "newexpr", + "omitzero", + "rangeint", + "reflecttypefor", + "slicescontains", + "slicessort", + "stditerators", + "stringscutprefix", + "stringsseq", + "stringsbuilder", + "testingcontext", + "waitgroup" + ] + }, + "wsl-checks": { + "enum": [ + "assign", + "branch", + "decl", + "defer", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "range", + "return", + "select", + "send", + "switch", + "type-switch", + "append", + "assign-exclusive", + "assign-expr", + "err", + "leading-whitespace", + "trailing-whitespace" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godoclint", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "iotamixing", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "modernize", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + }, + "comments-only": { + "description": "Checks only comments, skip strings.", + "type": "boolean", + "default": false + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "empty-line": { + "description": "Checks that there is an empty space between the embedded fields and regular fields.", + "type": "boolean", + "default": false + }, + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + }, + "force-tonot": { + "description": "Force using `ToNot`, `ShouldNot` instead of `To(Not())`.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godoclintSettings": { + "type": "object", + "properties": { + "default": { + "type": "string", + "enum": ["all", "basic", "none"], + "default": "basic", + "description": "Default set of rules to enable." + }, + "enable": { + "description": "List of rules to enable in addition to the default set.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "disable": { + "description": "List of rules to disable.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "options": { + "type": "object", + "description": "A map for setting individual rule options.", + "properties": { + "max-len": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "description": "Maximum line length for godocs, not including the `// `, or `/*` or `*/` tokens.", + "default": 77 + } + } + }, + "require-doc": { + "type": "object", + "properties": { + "ignore-exported": { + "type": "boolean", + "description": "Ignore exported (public) symbols when applying the `require-doc` rule.", + "default": false + }, + "ignore-unexported": { + "type": "boolean", + "description": "Ignore unexported (private) symbols when applying the `require-doc` rule.", + "default": true + } + } + }, + "start-with-name": { + "type": "object", + "properties": { + "include-unexported": { + "type": "boolean", + "description": "Include unexported symbols when applying the `start-with-name` rule.", + "default": false + } + } + } + } + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ineffassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-escaping-errors": { + "description": "Check escaping variables of type error, may cause false positives.", + "type": "boolean", + "default": false + } + } + }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "modernizeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "description": "List of analyzers to disable.", + "type": "array", + "items": { + "$ref": "#/definitions/modernize-analyzers" + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + }, + "concat-loop": { + "description": "Enable/disable optimization of concat loop.", + "type": "boolean", + "default": true + }, + "loop-other-ops": { + "description": "Optimization of `concat-loop` even with other operations.", + "type": "boolean", + "default": false + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unqueryvetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-sql-builders": { + "description": "Enable SQL builder checking.", + "type": "boolean", + "default": true + }, + "allowed-patterns": { + "description": "Regex patterns for acceptable SELECT * usage.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godoclint": { + "$ref": "#/definitions/settings/definitions/godoclintSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ineffassign": { + "$ref": "#/definitions/settings/definitions/ineffassignSettings" + }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "modernize": { + "$ref": "#/definitions/settings/definitions/modernizeSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unqueryvet": { + "$ref": "#/definitions/settings/definitions/unqueryvetSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Apply the fixes detected by the linters and formatters (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.7.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.7.jsonschema.json new file mode 100644 index 00000000..accf61f9 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.7.jsonschema.json @@ -0,0 +1,5144 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupOption", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr", + "zeroByteRepeat" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "godoclint-rules": { + "enum": [ + "pkg-doc", + "single-pkg-doc", + "require-pkg-doc", + "start-with-name", + "require-doc", + "deprecated", + "max-len", + "no-unused-link" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G114", + "G115", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "forbidden-call-in-wg-go", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "identical-ifelseif-branches", + "identical-ifelseif-conditions", + "identical-switch-branches", + "identical-switch-conditions", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "inefficient-map-lookup", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "package-directory-mismatch", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-if", + "unnecessary-stmt", + "unreachable-code", + "unsecure-url-scheme", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "use-waitgroup-go", + "useless-break", + "useless-fallthrough", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "modernize-analyzers": { + "enum": [ + "any", + "bloop", + "fmtappendf", + "forvar", + "mapsloop", + "minmax", + "newexpr", + "omitzero", + "plusbuild", + "rangeint", + "reflecttypefor", + "slicescontains", + "slicessort", + "stditerators", + "stringscutprefix", + "stringsseq", + "stringsbuilder", + "testingcontext", + "waitgroup" + ] + }, + "wsl-checks": { + "enum": [ + "assign", + "branch", + "decl", + "defer", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "range", + "return", + "select", + "send", + "switch", + "type-switch", + "append", + "assign-exclusive", + "assign-expr", + "err", + "leading-whitespace", + "trailing-whitespace" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godoclint", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "iotamixing", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "modernize", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + }, + "comments-only": { + "description": "Checks only comments, skip strings.", + "type": "boolean", + "default": false + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "empty-line": { + "description": "Checks that there is an empty space between the embedded fields and regular fields.", + "type": "boolean", + "default": false + }, + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + }, + "force-tonot": { + "description": "Force using `ToNot`, `ShouldNot` instead of `To(Not())`.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godoclintSettings": { + "type": "object", + "properties": { + "default": { + "type": "string", + "enum": ["all", "basic", "none"], + "default": "basic", + "description": "Default set of rules to enable." + }, + "enable": { + "description": "List of rules to enable in addition to the default set.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "disable": { + "description": "List of rules to disable.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "options": { + "type": "object", + "description": "A map for setting individual rule options.", + "properties": { + "max-len": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "description": "Maximum line length for godocs, not including the `// `, or `/*` or `*/` tokens.", + "default": 77 + } + } + }, + "require-doc": { + "type": "object", + "properties": { + "ignore-exported": { + "type": "boolean", + "description": "Ignore exported (public) symbols when applying the `require-doc` rule.", + "default": false + }, + "ignore-unexported": { + "type": "boolean", + "description": "Ignore unexported (private) symbols when applying the `require-doc` rule.", + "default": true + } + } + }, + "start-with-name": { + "type": "object", + "properties": { + "include-unexported": { + "type": "boolean", + "description": "Include unexported symbols when applying the `start-with-name` rule.", + "default": false + } + } + } + } + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ineffassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-escaping-errors": { + "description": "Check escaping variables of type error, may cause false positives.", + "type": "boolean", + "default": false + } + } + }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "modernizeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "description": "List of analyzers to disable.", + "type": "array", + "items": { + "$ref": "#/definitions/modernize-analyzers" + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + }, + "concat-loop": { + "description": "Enable/disable optimization of concat loop.", + "type": "boolean", + "default": true + }, + "loop-other-ops": { + "description": "Optimization of `concat-loop` even with other operations.", + "type": "boolean", + "default": false + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "enable-default-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unqueryvetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-sql-builders": { + "description": "Enable SQL builder checking.", + "type": "boolean", + "default": true + }, + "allowed-patterns": { + "description": "Regex patterns for acceptable SELECT * usage.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godoclint": { + "$ref": "#/definitions/settings/definitions/godoclintSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ineffassign": { + "$ref": "#/definitions/settings/definitions/ineffassignSettings" + }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "modernize": { + "$ref": "#/definitions/settings/definitions/modernizeSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unqueryvet": { + "$ref": "#/definitions/settings/definitions/unqueryvetSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Apply the fixes detected by the linters and formatters (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.8.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.8.jsonschema.json new file mode 100644 index 00000000..675beedd --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.8.jsonschema.json @@ -0,0 +1,5223 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupOption", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr", + "zeroByteRepeat" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "godoclint-rules": { + "enum": [ + "pkg-doc", + "single-pkg-doc", + "require-pkg-doc", + "start-with-name", + "require-doc", + "deprecated", + "max-len", + "no-unused-link", + "require-stdlib-doclink" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G114", + "G115", + "G116", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "forbidden-call-in-wg-go", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "identical-ifelseif-branches", + "identical-ifelseif-conditions", + "identical-switch-branches", + "identical-switch-conditions", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "inefficient-map-lookup", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "package-directory-mismatch", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-if", + "unnecessary-stmt", + "unreachable-code", + "unsecure-url-scheme", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "use-waitgroup-go", + "useless-break", + "useless-fallthrough", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "modernize-analyzers": { + "enum": [ + "any", + "bloop", + "fmtappendf", + "forvar", + "mapsloop", + "minmax", + "newexpr", + "omitzero", + "plusbuild", + "rangeint", + "reflecttypefor", + "slicescontains", + "slicessort", + "stditerators", + "stringscut", + "stringscutprefix", + "stringsseq", + "stringsbuilder", + "testingcontext", + "unsafefuncs", + "waitgroup" + ] + }, + "wsl-checks": { + "enum": [ + "assign", + "branch", + "decl", + "defer", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "range", + "return", + "select", + "send", + "switch", + "type-switch", + "append", + "assign-exclusive", + "assign-expr", + "err", + "leading-whitespace", + "trailing-whitespace" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godoclint", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "iotamixing", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "modernize", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + }, + "comments-only": { + "description": "Checks only comments, skip strings.", + "type": "boolean", + "default": false + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "empty-line": { + "description": "Checks that there is an empty space between the embedded fields and regular fields.", + "type": "boolean", + "default": false + }, + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + }, + "force-tonot": { + "description": "Force using `ToNot`, `ShouldNot` instead of `To(Not())`.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godoclintSettings": { + "type": "object", + "properties": { + "default": { + "type": "string", + "enum": ["all", "basic", "none"], + "default": "basic", + "description": "Default set of rules to enable." + }, + "enable": { + "description": "List of rules to enable in addition to the default set.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "disable": { + "description": "List of rules to disable.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "options": { + "type": "object", + "description": "A map for setting individual rule options.", + "properties": { + "max-len": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "description": "Maximum line length for godocs, not including the `//`, `/*` or `*/` tokens.", + "default": 77 + } + } + }, + "require-doc": { + "type": "object", + "properties": { + "ignore-exported": { + "type": "boolean", + "description": "Ignore exported (public) symbols when applying the `require-doc` rule.", + "default": false + }, + "ignore-unexported": { + "type": "boolean", + "description": "Ignore unexported (private) symbols when applying the `require-doc` rule.", + "default": true + } + } + }, + "start-with-name": { + "type": "object", + "properties": { + "include-unexported": { + "type": "boolean", + "description": "Include unexported symbols when applying the `start-with-name` rule.", + "default": false + } + } + } + } + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + }, + "check-module-path": { + "description": "Check the validity of the module path.", + "type": "boolean", + "default": false + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ineffassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-escaping-errors": { + "description": "Check escaping variables of type error, may cause false positives.", + "type": "boolean", + "default": false + } + } + }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "modernizeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "description": "List of analyzers to disable.", + "type": "array", + "items": { + "$ref": "#/definitions/modernize-analyzers" + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + }, + "concat-loop": { + "description": "Enable/disable optimization of concat loop.", + "type": "boolean", + "default": true + }, + "loop-other-ops": { + "description": "Optimization of `concat-loop` even with other operations.", + "type": "boolean", + "default": false + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "enable-default-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unqueryvetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-sql-builders": { + "description": "Enable SQL builder checking.", + "type": "boolean", + "default": true + }, + "check-aliased-wildcard": { + "description": "Enable aliased wildcard detection like SELECT t.*.", + "type": "boolean", + "default": true + }, + "check-string-concat": { + "description": "Enable string concatenation analysis.", + "type": "boolean", + "default": true + }, + "check-format-strings": { + "description": "Enable format string analysis like fmt.Sprintf.", + "type": "boolean", + "default": true + }, + "check-string-builder": { + "description": "Enable strings.Builder analysis.", + "type": "boolean", + "default": true + }, + "check-subqueries": { + "description": "Enable subquery analysis.", + "type": "boolean", + "default": true + }, + "allowed-patterns": { + "description": "Regex patterns for acceptable SELECT * usage.", + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Functions to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "sql-builders": { + "type": "object", + "additionalProperties": false, + "properties": { + "squirrel": { + "type": "boolean", + "default": true + }, + "gorm": { + "type": "boolean", + "default": true + }, + "sqlx": { + "type": "boolean", + "default": true + }, + "ent": { + "type": "boolean", + "default": true + }, + "pgx": { + "type": "boolean", + "default": true + }, + "bun": { + "type": "boolean", + "default": true + }, + "sqlboiler": { + "type": "boolean", + "default": true + }, + "jet": { + "type": "boolean", + "default": true + } + } + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godoclint": { + "$ref": "#/definitions/settings/definitions/godoclintSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ineffassign": { + "$ref": "#/definitions/settings/definitions/ineffassignSettings" + }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "modernize": { + "$ref": "#/definitions/settings/definitions/modernizeSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unqueryvet": { + "$ref": "#/definitions/settings/definitions/unqueryvetSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Apply the fixes detected by the linters and formatters (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.9.jsonschema.json b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.9.jsonschema.json new file mode 100644 index 00000000..38b3590c --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/golangci.v2.9.jsonschema.json @@ -0,0 +1,5278 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/golangci-lint.json", + "definitions": { + "gocritic-checks": { + "enum": [ + "appendAssign", + "appendCombine", + "argOrder", + "assignOp", + "badCall", + "badCond", + "badLock", + "badRegexp", + "badSorting", + "badSyncOnceFunc", + "boolExprSimplify", + "builtinShadow", + "builtinShadowDecl", + "captLocal", + "caseOrder", + "codegenComment", + "commentFormatting", + "commentedOutCode", + "commentedOutImport", + "defaultCaseOrder", + "deferInLoop", + "deferUnlambda", + "deprecatedComment", + "docStub", + "dupArg", + "dupBranchBody", + "dupCase", + "dupImport", + "dupOption", + "dupSubExpr", + "dynamicFmtString", + "elseif", + "emptyDecl", + "emptyFallthrough", + "emptyStringTest", + "equalFold", + "evalOrder", + "exitAfterDefer", + "exposedSyncMutex", + "externalErrorReassign", + "filepathJoin", + "flagDeref", + "flagName", + "hexLiteral", + "httpNoBody", + "hugeParam", + "ifElseChain", + "importShadow", + "indexAlloc", + "initClause", + "mapKey", + "methodExprCall", + "nestingReduce", + "newDeref", + "nilValReturn", + "octalLiteral", + "offBy1", + "paramTypeCombine", + "preferDecodeRune", + "preferFilepathJoin", + "preferFprint", + "preferStringWriter", + "preferWriteByte", + "ptrToRefParam", + "rangeAppendAll", + "rangeExprCopy", + "rangeValCopy", + "redundantSprint", + "regexpMust", + "regexpPattern", + "regexpSimplify", + "returnAfterHttpError", + "ruleguard", + "singleCaseSwitch", + "sliceClear", + "sloppyLen", + "sloppyReassign", + "sloppyTypeAssert", + "sortSlice", + "sprintfQuotedString", + "sqlQuery", + "stringConcatSimplify", + "stringXbytes", + "stringsCompare", + "switchTrue", + "syncMapLoadAndDelete", + "timeExprSimplify", + "todoCommentWithoutDetail", + "tooManyResultsChecker", + "truncateCmp", + "typeAssertChain", + "typeDefFirst", + "typeSwitchVar", + "typeUnparen", + "uncheckedInlineErr", + "underef", + "unlabelStmt", + "unlambda", + "unnamedResult", + "unnecessaryBlock", + "unnecessaryDefer", + "unslice", + "valSwap", + "weakCond", + "whyNoLint", + "wrapperFunc", + "yodaStyleExpr", + "zeroByteRepeat" + ] + }, + "gocritic-tags": { + "enum": [ + "diagnostic", + "style", + "performance", + "experimental", + "opinionated", + "security" + ] + }, + "staticcheck-checks": { + "enum": [ + "*", + "all", + "SA*", + "-SA*", + "SA1*", + "-SA1*", + "SA1000", + "-SA1000", + "SA1001", + "-SA1001", + "SA1002", + "-SA1002", + "SA1003", + "-SA1003", + "SA1004", + "-SA1004", + "SA1005", + "-SA1005", + "SA1006", + "-SA1006", + "SA1007", + "-SA1007", + "SA1008", + "-SA1008", + "SA1010", + "-SA1010", + "SA1011", + "-SA1011", + "SA1012", + "-SA1012", + "SA1013", + "-SA1013", + "SA1014", + "-SA1014", + "SA1015", + "-SA1015", + "SA1016", + "-SA1016", + "SA1017", + "-SA1017", + "SA1018", + "-SA1018", + "SA1019", + "-SA1019", + "SA1020", + "-SA1020", + "SA1021", + "-SA1021", + "SA1023", + "-SA1023", + "SA1024", + "-SA1024", + "SA1025", + "-SA1025", + "SA1026", + "-SA1026", + "SA1027", + "-SA1027", + "SA1028", + "-SA1028", + "SA1029", + "-SA1029", + "SA1030", + "-SA1030", + "SA1031", + "-SA1031", + "SA1032", + "-SA1032", + "SA2*", + "-SA2*", + "SA2000", + "-SA2000", + "SA2001", + "-SA2001", + "SA2002", + "-SA2002", + "SA2003", + "-SA2003", + "SA3*", + "-SA3*", + "SA3000", + "-SA3000", + "SA3001", + "-SA3001", + "SA4*", + "-SA4*", + "SA4000", + "-SA4000", + "SA4001", + "-SA4001", + "SA4003", + "-SA4003", + "SA4004", + "-SA4004", + "SA4005", + "-SA4005", + "SA4006", + "-SA4006", + "SA4008", + "-SA4008", + "SA4009", + "-SA4009", + "SA4010", + "-SA4010", + "SA4011", + "-SA4011", + "SA4012", + "-SA4012", + "SA4013", + "-SA4013", + "SA4014", + "-SA4014", + "SA4015", + "-SA4015", + "SA4016", + "-SA4016", + "SA4017", + "-SA4017", + "SA4018", + "-SA4018", + "SA4019", + "-SA4019", + "SA4020", + "-SA4020", + "SA4021", + "-SA4021", + "SA4022", + "-SA4022", + "SA4023", + "-SA4023", + "SA4024", + "-SA4024", + "SA4025", + "-SA4025", + "SA4026", + "-SA4026", + "SA4027", + "-SA4027", + "SA4028", + "-SA4028", + "SA4029", + "-SA4029", + "SA4030", + "-SA4030", + "SA4031", + "-SA4031", + "SA4032", + "-SA4032", + "SA5*", + "-SA5*", + "SA5000", + "-SA5000", + "SA5001", + "-SA5001", + "SA5002", + "-SA5002", + "SA5003", + "-SA5003", + "SA5004", + "-SA5004", + "SA5005", + "-SA5005", + "SA5007", + "-SA5007", + "SA5008", + "-SA5008", + "SA5009", + "-SA5009", + "SA5010", + "-SA5010", + "SA5011", + "-SA5011", + "SA5012", + "-SA5012", + "SA6*", + "-SA6*", + "SA6000", + "-SA6000", + "SA6001", + "-SA6001", + "SA6002", + "-SA6002", + "SA6003", + "-SA6003", + "SA6005", + "-SA6005", + "SA6006", + "-SA6006", + "SA9*", + "-SA9*", + "SA9001", + "-SA9001", + "SA9002", + "-SA9002", + "SA9003", + "-SA9003", + "SA9004", + "-SA9004", + "SA9005", + "-SA9005", + "SA9006", + "-SA9006", + "SA9007", + "-SA9007", + "SA9008", + "-SA9008", + "SA9009", + "-SA9009", + "ST*", + "-ST*", + "ST1*", + "-ST1*", + "ST1000", + "-ST1000", + "ST1001", + "-ST1001", + "ST1003", + "-ST1003", + "ST1005", + "-ST1005", + "ST1006", + "-ST1006", + "ST1008", + "-ST1008", + "ST1011", + "-ST1011", + "ST1012", + "-ST1012", + "ST1013", + "-ST1013", + "ST1015", + "-ST1015", + "ST1016", + "-ST1016", + "ST1017", + "-ST1017", + "ST1018", + "-ST1018", + "ST1019", + "-ST1019", + "ST1020", + "-ST1020", + "ST1021", + "-ST1021", + "ST1022", + "-ST1022", + "ST1023", + "-ST1023", + "S*", + "-S*", + "S1*", + "-S1*", + "S1000", + "-S1000", + "S1001", + "-S1001", + "S1002", + "-S1002", + "S1003", + "-S1003", + "S1004", + "-S1004", + "S1005", + "-S1005", + "S1006", + "-S1006", + "S1007", + "-S1007", + "S1008", + "-S1008", + "S1009", + "-S1009", + "S1010", + "-S1010", + "S1011", + "-S1011", + "S1012", + "-S1012", + "S1016", + "-S1016", + "S1017", + "-S1017", + "S1018", + "-S1018", + "S1019", + "-S1019", + "S1020", + "-S1020", + "S1021", + "-S1021", + "S1023", + "-S1023", + "S1024", + "-S1024", + "S1025", + "-S1025", + "S1028", + "-S1028", + "S1029", + "-S1029", + "S1030", + "-S1030", + "S1031", + "-S1031", + "S1032", + "-S1032", + "S1033", + "-S1033", + "S1034", + "-S1034", + "S1035", + "-S1035", + "S1036", + "-S1036", + "S1037", + "-S1037", + "S1038", + "-S1038", + "S1039", + "-S1039", + "S1040", + "-S1040", + "QF*", + "-QF*", + "QF1*", + "-QF1*", + "QF1001", + "-QF1001", + "QF1002", + "-QF1002", + "QF1003", + "-QF1003", + "QF1004", + "-QF1004", + "QF1005", + "-QF1005", + "QF1006", + "-QF1006", + "QF1007", + "-QF1007", + "QF1008", + "-QF1008", + "QF1009", + "-QF1009", + "QF1010", + "-QF1010", + "QF1011", + "-QF1011", + "QF1012", + "-QF1012" + ] + }, + "godoclint-rules": { + "enum": [ + "pkg-doc", + "single-pkg-doc", + "require-pkg-doc", + "start-with-name", + "require-doc", + "deprecated", + "max-len", + "no-unused-link", + "require-stdlib-doclink" + ] + }, + "gosec-rules": { + "enum": [ + "G101", + "G102", + "G103", + "G104", + "G106", + "G107", + "G108", + "G109", + "G110", + "G111", + "G112", + "G114", + "G115", + "G116", + "G201", + "G202", + "G203", + "G204", + "G301", + "G302", + "G303", + "G304", + "G305", + "G306", + "G307", + "G401", + "G402", + "G403", + "G404", + "G405", + "G406", + "G501", + "G502", + "G503", + "G504", + "G505", + "G506", + "G507", + "G601", + "G602" + ] + }, + "govet-analyzers": { + "enum": [ + "appends", + "asmdecl", + "assign", + "atomic", + "atomicalign", + "bools", + "buildtag", + "cgocall", + "composites", + "copylocks", + "deepequalerrors", + "defers", + "directive", + "errorsas", + "fieldalignment", + "findcall", + "framepointer", + "hostport", + "httpmux", + "httpresponse", + "ifaceassert", + "loopclosure", + "lostcancel", + "nilfunc", + "nilness", + "printf", + "reflectvaluecompare", + "shadow", + "shift", + "sigchanyzer", + "slog", + "sortslice", + "stdmethods", + "stdversion", + "stringintconv", + "structtag", + "testinggoroutine", + "tests", + "timeformat", + "unmarshal", + "unreachable", + "unsafeptr", + "unusedresult", + "unusedwrite", + "waitgroup" + ] + }, + "revive-rules": { + "enum": [ + "add-constant", + "argument-limit", + "atomic", + "banned-characters", + "bare-return", + "blank-imports", + "bool-literal-in-expr", + "call-to-gc", + "cognitive-complexity", + "comment-spacings", + "comments-density", + "confusing-naming", + "confusing-results", + "constant-logical-expr", + "context-as-argument", + "context-keys-type", + "cyclomatic", + "datarace", + "deep-exit", + "defer", + "dot-imports", + "duplicated-imports", + "early-return", + "empty-block", + "empty-lines", + "enforce-map-style", + "enforce-repeated-arg-type-style", + "enforce-slice-style", + "enforce-switch-style", + "epoch-naming", + "error-naming", + "error-return", + "error-strings", + "errorf", + "exported", + "file-header", + "file-length-limit", + "filename-format", + "flag-parameter", + "forbidden-call-in-wg-go", + "function-length", + "function-result-limit", + "get-return", + "identical-branches", + "identical-ifelseif-branches", + "identical-ifelseif-conditions", + "identical-switch-branches", + "identical-switch-conditions", + "if-return", + "import-alias-naming", + "import-shadowing", + "imports-blocklist", + "increment-decrement", + "indent-error-flow", + "inefficient-map-lookup", + "line-length-limit", + "max-control-nesting", + "max-public-structs", + "modifies-parameter", + "modifies-value-receiver", + "nested-structs", + "optimize-operands-order", + "package-comments", + "package-directory-mismatch", + "range-val-address", + "range-val-in-closure", + "range", + "receiver-naming", + "redefines-builtin-id", + "redundant-build-tag", + "redundant-import-alias", + "redundant-test-main-exit", + "string-format", + "string-of-int", + "struct-tag", + "superfluous-else", + "time-date", + "time-equal", + "time-naming", + "unchecked-type-assertion", + "unconditional-recursion", + "unexported-naming", + "unexported-return", + "unhandled-error", + "unnecessary-format", + "unnecessary-if", + "unnecessary-stmt", + "unreachable-code", + "unsecure-url-scheme", + "unused-parameter", + "unused-receiver", + "use-any", + "use-errors-new", + "use-fmt-print", + "use-slices-sort", + "use-waitgroup-go", + "useless-break", + "useless-fallthrough", + "var-declaration", + "var-naming", + "waitgroup-by-value" + ] + }, + "iface-analyzers": { + "enum": [ + "identical", + "unused", + "opaque", + "unexported" + ] + }, + "tagliatelle-cases": { + "enum": [ + "", + "camel", + "pascal", + "kebab", + "snake", + "goCamel", + "goPascal", + "goKebab", + "goSnake", + "upper", + "upperSnake", + "lower", + "header" + ] + }, + "modernize-analyzers": { + "enum": [ + "any", + "fmtappendf", + "forvar", + "mapsloop", + "minmax", + "newexpr", + "omitzero", + "plusbuild", + "rangeint", + "reflecttypefor", + "slicescontains", + "slicessort", + "stditerators", + "stringscut", + "stringscutprefix", + "stringsseq", + "stringsbuilder", + "testingcontext", + "unsafefuncs", + "waitgroup" + ] + }, + "wsl-checks": { + "enum": [ + "assign", + "branch", + "decl", + "defer", + "expr", + "for", + "go", + "if", + "inc-dec", + "label", + "range", + "return", + "select", + "send", + "switch", + "type-switch", + "append", + "assign-exclusive", + "assign-expr", + "err", + "leading-whitespace", + "trailing-whitespace", + "after-block" + ] + }, + "relative-path-modes": { + "enum": [ + "gomod", + "gitroot", + "cfg", + "wd" + ] + }, + "simple-format": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + } + } + }, + "formats-path" : { + "anyOf": [ + { + "enum": [ + "stdout", + "stderr" + ] + }, + { + "type": "string" + } + ] + }, + "linter-names": { + "$comment": "anyOf with enum is used to allow auto-completion of non-custom linters", + "description": "Usable linter names.", + "anyOf": [ + { + "enum": [ + "arangolint", + "asasalint", + "asciicheck", + "bidichk", + "bodyclose", + "canonicalheader", + "containedctx", + "contextcheck", + "copyloopvar", + "cyclop", + "decorder", + "depguard", + "dogsled", + "dupl", + "dupword", + "durationcheck", + "embeddedstructfieldcheck", + "errcheck", + "errchkjson", + "errname", + "errorlint", + "exhaustive", + "exhaustruct", + "exptostd", + "fatcontext", + "forbidigo", + "forcetypeassert", + "funcorder", + "funlen", + "ginkgolinter", + "gocheckcompilerdirectives", + "gochecknoglobals", + "gochecknoinits", + "gochecksumtype", + "gocognit", + "goconst", + "gocritic", + "gocyclo", + "godoclint", + "godot", + "godox", + "err113", + "goheader", + "gomoddirectives", + "gomodguard", + "goprintffuncname", + "gosec", + "gosimple", + "gosmopolitan", + "govet", + "grouper", + "iface", + "importas", + "inamedparam", + "ineffassign", + "interfacebloat", + "intrange", + "iotamixing", + "ireturn", + "lll", + "loggercheck", + "maintidx", + "makezero", + "mirror", + "misspell", + "mnd", + "modernize", + "musttag", + "nakedret", + "nestif", + "nilerr", + "nilnesserr", + "nilnil", + "nlreturn", + "noctx", + "noinlineerr", + "nolintlint", + "nonamedreturns", + "nosprintfhostport", + "paralleltest", + "perfsprint", + "prealloc", + "predeclared", + "promlinter", + "protogetter", + "reassign", + "recvcheck", + "revive", + "rowserrcheck", + "sloglint", + "sqlclosecheck", + "staticcheck", + "stylecheck", + "tagalign", + "tagliatelle", + "testableexamples", + "testifylint", + "testpackage", + "thelper", + "tparallel", + "unconvert", + "unparam", + "unused", + "usestdlibvars", + "usetesting", + "varnamelen", + "wastedassign", + "whitespace", + "wrapcheck", + "wsl", + "wsl_v5", + "zerologlint" + ] + }, + { + "type": "string" + } + ] + }, + "formatter-names": { + "description": "Usable formatter names.", + "enum": [ + "gci", + "gofmt", + "gofumpt", + "goimports", + "golines", + "swaggo" + ] + }, + "settings": { + "definitions": { + "dupwordSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Keywords for detecting duplicate words. If this list is not empty, only the words defined in this list will be detected.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["the", "and", "a"] + } + }, + "ignore": { + "description": "Keywords used to ignore detection.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["0C0C"] + } + }, + "comments-only": { + "description": "Checks only comments, skip strings.", + "type": "boolean", + "default": false + } + } + }, + "asasalintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "description": "To specify a set of function names to exclude.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["\\.Wrapf"] + } + }, + "use-builtin-exclusions": { + "description": "To enable/disable the asasalint builtin exclusions of function names.", + "type": "boolean", + "default": true + } + } + }, + "bidichkSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "left-to-right-embedding": { + "description": "Disallow: LEFT-TO-RIGHT-EMBEDDING", + "type": "boolean", + "default": false + }, + "right-to-left-embedding": { + "description": "Disallow: RIGHT-TO-LEFT-EMBEDDING", + "type": "boolean", + "default": false + }, + "pop-directional-formatting": { + "description": "Disallow: POP-DIRECTIONAL-FORMATTING", + "type": "boolean", + "default": false + }, + "left-to-right-override": { + "description": "Disallow: LEFT-TO-RIGHT-OVERRIDE", + "type": "boolean", + "default": false + }, + "right-to-left-override": { + "description": "Disallow: RIGHT-TO-LEFT-OVERRIDE", + "type": "boolean", + "default": false + }, + "left-to-right-isolate": { + "description": "Disallow: LEFT-TO-RIGHT-ISOLATE", + "type": "boolean", + "default": false + }, + "right-to-left-isolate": { + "description": "Disallow: RIGHT-TO-LEFT-ISOLATE", + "type": "boolean", + "default": false + }, + "first-strong-isolate": { + "description": "Disallow: FIRST-STRONG-ISOLATE", + "type": "boolean", + "default": false + }, + "pop-directional-isolate": { + "description": "Disallow: POP-DIRECTIONAL-ISOLATE", + "type": "boolean", + "default": false + } + } + }, + "cyclopSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-complexity": { + "description": "Max complexity the function can have", + "type": "integer", + "default": 10, + "minimum": 0 + }, + "package-average": { + "description": "Max average complexity in package", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "decorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "dec-order": { + "type": "array", + "default": [["type", "const", "var", "func"]], + "items": { + "enum": ["type", "const", "var", "func"] + } + }, + "ignore-underscore-vars": { + "description": "Underscore vars (vars with \"_\" as the name) will be ignored at all checks", + "default": true, + "type": "boolean" + }, + "disable-dec-order-check": { + "description": "Order of declarations is not checked", + "default": true, + "type": "boolean" + }, + "disable-init-func-first-check": { + "description": "Allow init func to be anywhere in file", + "default": true, + "type": "boolean" + }, + "disable-dec-num-check": { + "description": "Multiple global type, const and var declarations are allowed", + "default": true, + "type": "boolean" + }, + "disable-type-dec-num-check": { + "description": "Type declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-const-dec-num-check": { + "description": "Const declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + }, + "disable-var-dec-num-check": { + "description": "Var declarations will be ignored for dec num check", + "default": true, + "type": "boolean" + } + } + }, + "depguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "rules": { + "description": "Rules to apply.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[^.]+$": { + "description": "Name of a rule.", + "type": "object", + "additionalProperties": false, + "properties": { + "list-mode": { + "description": "Used to determine the package matching priority.", + "enum": ["original", "strict", "lax"], + "default": "original" + }, + "files": { + "description": "List of file globs that will match this list of settings to compare against.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "List of allowed packages.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "string" + } + }, + "deny": { + "description": "Packages that are not allowed where the value is a suggestion.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "desc": { + "description": "Description", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "dogsledSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-blank-identifiers": { + "description": "Check assignments with too many blank identifiers.", + "type": "integer", + "default": 2, + "minimum": 0 + } + } + }, + "duplSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "threshold": { + "description": "Tokens count to trigger issue.", + "type": "integer", + "default": 150, + "minimum": 0 + } + } + }, + "embeddedstructfieldcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "empty-line": { + "description": "Checks that there is an empty space between the embedded fields and regular fields.", + "type": "boolean", + "default": false + }, + "forbid-mutex": { + "description": "Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.", + "type": "boolean", + "default": false + } + } + }, + "errcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-type-assertions": { + "description": "Report about not checking errors in type assertions, i.e.: `a := b.(MyStruct)`", + "type": "boolean", + "default": false + }, + "check-blank": { + "description": "Report about assignment of errors to blank identifier", + "type": "boolean", + "default": false + }, + "exclude-functions": { + "description": "List of functions to exclude from checking, where each entry is a single function to exclude", + "type": "array", + "examples": ["io/ioutil.ReadFile", "io.Copy(*bytes.Buffer)"], + "items": { + "type": "string" + } + }, + "disable-default-exclusions": { + "description": "To disable the errcheck built-in exclude list", + "type": "boolean", + "default": false + }, + "verbose": { + "description": "Display function signature instead of selector", + "type": "boolean", + "default": false + } + } + }, + "errchkjsonSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-error-free-encoding": { + "type": "boolean", + "default": false + }, + "report-no-exported": { + "description": "Issue on struct that doesn't have exported fields.", + "type": "boolean", + "default": false + } + } + }, + "errorlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "errorf": { + "description": "Check whether fmt.Errorf uses the %w verb for formatting errors", + "type": "boolean", + "default": true + }, + "errorf-multi": { + "description": "Permit more than 1 %w verb, valid per Go 1.20", + "type": "boolean", + "default": true + }, + "asserts": { + "description": "Check for plain type assertions and type switches.", + "type": "boolean", + "default": true + }, + "comparison": { + "description": "Check for plain error comparisons", + "type": "boolean", + "default": true + }, + "allowed-errors": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + }, + "allowed-errors-wildcard": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "err": { + "type": "string" + }, + "fun": { + "type": "string" + } + } + } + } + } + }, + "exhaustiveSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check": { + "description": "Program elements to check for exhaustiveness.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["switch", "map"] + } + }, + "explicit-exhaustive-switch": { + "description": "Only run exhaustive check on switches with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "explicit-exhaustive-map": { + "description": "Only run exhaustive check on map literals with \"//exhaustive:enforce\" comment.", + "type": "boolean", + "default": false + }, + "default-case-required": { + "description": "Switch statement requires default case even if exhaustive.", + "type": "boolean", + "default": false + }, + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, even if all enum members are not listed.", + "type": "boolean", + "default": false + }, + "ignore-enum-members": { + "description": "Enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness", + "type": "string" + }, + "ignore-enum-types": { + "description": "Enum types matching the supplied regex do not have to be listed in switch statements to satisfy exhaustiveness.", + "type": "string" + }, + "package-scope-only": { + "description": "Consider enums only in package scopes, not in inner scopes.", + "type": "boolean", + "default": false + } + } + }, + "exhaustructSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "description": "List of regular expressions to match struct packages and names.", + "type": "array", + "examples": [".*\\.Test"], + "items": { + "type": "string" + } + }, + "exclude": { + "description": "List of regular expressions to exclude struct packages and names from check.", + "type": "array", + "examples": ["cobra\\.Command$"], + "items": { + "type": "string" + } + }, + "allow-empty": { + "description": "Allows empty structures, effectively excluding them from the check.", + "type": "boolean", + "default": false + }, + "allow-empty-rx": { + "description": "List of regular expressions to match type names that should be allowed to be empty.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-empty-returns": { + "description": "Allows empty structures in return statements.", + "type": "boolean", + "default": false + }, + "allow-empty-declarations": { + "description": "Allows empty structures in variable declarations.", + "type": "boolean", + "default": false + } + } + }, + "fatcontextSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-struct-pointers": { + "description": "Check for potential fat contexts in struct pointers.", + "type": "boolean", + "default": false + } + } + }, + "forbidigoSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude-godoc-examples": { + "description": "Exclude code in godoc examples.", + "type": "boolean", + "default": true + }, + "analyze-types": { + "description": "Instead of matching the literal source code, use type information to replace expressions with strings that contain the package name and (for methods and fields) the type name.", + "type": "boolean", + "default": true + }, + "forbid": { + "description": "List of identifiers to forbid (written using `regexp`)", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Pattern", + "type": "string" + }, + "pkg": { + "description": "Package", + "type": "string" + }, + "msg": { + "description": "Message", + "type": "string" + } + } + } + } + } + }, + "funcorderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "constructor": { + "description": "Checks that constructors are placed after the structure declaration.", + "type": "boolean", + "default": true + }, + "struct-method": { + "description": "Checks if the exported methods of a structure are placed before the non-exported ones.", + "type": "boolean", + "default": true + }, + "alphabetical": { + "description": "Checks if the constructors and/or structure methods are sorted alphabetically.", + "type": "boolean", + "default": false + } + } + }, + "funlenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "lines": { + "description": "Limit lines number per function.", + "type": "integer", + "default": 60 + }, + "statements": { + "description": "Limit statements number per function.", + "type": "integer", + "default": 40 + }, + "ignore-comments": { + "description": "Ignore comments when counting lines.", + "type": "boolean", + "default": true + } + } + }, + "gciSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "sections": { + "description": "Section configuration to compare against.", + "type": "array", + "items": { + "anyOf": [ + { + "enum": [ + "standard", + "default", + "blank", + "dot", + "alias", + "localmodule" + ] + }, + { + "type": "string" + } + ] + }, + "default": ["standard", "default"] + }, + "no-inline-comments": { + "description": "Checks that no inline Comments are present.", + "type": "boolean", + "default": false + }, + "no-prefix-comments": { + "description": "Checks that no prefix Comments(comment lines above an import) are present.", + "type": "boolean", + "default": false + }, + "custom-order": { + "description": "Enable custom order of sections.", + "type": "boolean", + "default": false + }, + "no-lex-order": { + "description": "Drops lexical ordering for custom sections.", + "type": "boolean", + "default": false + } + } + }, + "ginkgolinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "suppress-len-assertion": { + "description": "Suppress the wrong length assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-nil-assertion": { + "description": "Suppress the wrong nil assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-err-assertion": { + "description": "Suppress the wrong error assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-compare-assertion": { + "description": "Suppress the wrong comparison assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-async-assertion": { + "description": "Suppress the function all in async assertion warning.", + "type": "boolean", + "default": false + }, + "suppress-type-compare-assertion": { + "description": "Suppress warning for comparing values from different types, like int32 and uint32.", + "type": "boolean", + "default": false + }, + "forbid-focus-container": { + "description": "Trigger warning for ginkgo focus containers like FDescribe, FContext, FWhen or FIt.", + "type": "boolean", + "default": false + }, + "allow-havelen-zero": { + "description": "Don't trigger warnings for HaveLen(0).", + "type": "boolean", + "default": false + }, + "force-expect-to": { + "description": "Force using `Expect` with `To`, `ToNot` or `NotTo`", + "type": "boolean", + "default": false + }, + "validate-async-intervals": { + "description": "Best effort validation of async intervals (timeout and polling).", + "type": "boolean", + "default": false + }, + "forbid-spec-pollution": { + "description": "Trigger a warning for variable assignments in ginkgo containers like `Describe`, `Context` and `When`, instead of in `BeforeEach()`.", + "type": "boolean", + "default": false + }, + "force-succeed": { + "description": "Force using the Succeed matcher for error functions, and the HaveOccurred matcher for non-function error values.", + "type": "boolean", + "default": false + }, + "force-assertion-description": { + "description": "Force adding assertion descriptions to gomega matchers.", + "type": "boolean", + "default": false + }, + "force-tonot": { + "description": "Force using `ToNot`, `ShouldNot` instead of `To(Not())`.", + "type": "boolean", + "default": false + } + } + }, + "gochecksumtypeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "default-signifies-exhaustive": { + "description": "Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.", + "type": "boolean", + "default": true + }, + "include-shared-interfaces": { + "description": "Include shared interfaces in the exhaustiviness check.", + "type": "boolean", + "default": false + } + } + }, + "gocognitSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimal code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "goconstSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "match-constant": { + "description": "Look for existing constants matching the values", + "type": "boolean", + "default": true + }, + "min-len": { + "description": "Minimum length of string constant.", + "type": "integer", + "default": 3 + }, + "min-occurrences": { + "description": "Minimum occurrences count to trigger.", + "type": "integer", + "default": 3 + }, + "ignore-calls": { + "description": "Ignore when constant is not used as function argument", + "type": "boolean", + "default": true + }, + "ignore-string-values": { + "description": "Exclude strings matching the given regular expression", + "type": "array", + "items": { + "type": "string" + } + }, + "numbers": { + "description": "Search also for duplicated numbers.", + "type": "boolean", + "default": false + }, + "min": { + "description": "Minimum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "max": { + "description": "Maximum value, only works with `numbers`", + "type": "integer", + "default": 3 + }, + "find-duplicates": { + "description": "Detects constants with identical values", + "type": "boolean", + "default": false + }, + "eval-const-expressions": { + "description": "Evaluates of constant expressions like Prefix + \"suffix\"", + "type": "boolean", + "default": false + } + } + }, + "gocriticSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled-checks": { + "description": "Which checks should be enabled. By default, a list of stable checks is used. To see it, run `GL_DEBUG=gocritic golangci-lint run`.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + } + }, + "disabled-checks": { + "description": "Which checks should be disabled.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-checks" + }, + "default": [] + }, + "enabled-tags": { + "description": "Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "disabled-tags": { + "description": "Disable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.", + "type": "array", + "items": { + "$ref": "#/definitions/gocritic-tags" + } + }, + "settings": { + "description": "Settings passed to gocritic. Properties must be valid and enabled check names.", + "type": "object", + "additionalProperties": false, + "properties": { + "captLocal": { + "type": "object", + "additionalProperties": false, + "properties": { + "paramsOnly" : { + "type": "boolean", + "default": true + } + } + }, + "commentedOutCode": { + "type": "object", + "additionalProperties": false, + "properties": { + "minLength" : { + "type": "number", + "default": 15 + } + } + }, + "elseif": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipBalanced" : { + "type": "boolean", + "default": true + } + } + }, + "hugeParam": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 80 + } + } + }, + "ifElseChain": { + "type": "object", + "additionalProperties": false, + "properties": { + "minThreshold" : { + "type": "number", + "default": 2 + } + } + }, + "nestingReduce": { + "type": "object", + "additionalProperties": false, + "properties": { + "bodyWidth" : { + "type": "number", + "default": 5 + } + } + }, + "rangeExprCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 512 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "rangeValCopy": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizeThreshold" : { + "type": "number", + "default": 128 + }, + "skipTestFuncs" : { + "type": "boolean", + "default": true + } + } + }, + "ruleguard": { + "type": "object", + "additionalProperties": false, + "properties": { + "debug" : { + "type": "string" + }, + "enable" : { + "type": "string" + }, + "disable" : { + "type": "string" + }, + "failOn" : { + "type": "string" + }, + "rules" : { + "type": "string" + } + } + }, + "tooManyResultsChecker": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxResults" : { + "type": "number", + "default": 5 + } + } + }, + "truncateCmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipArchDependent" : { + "type": "boolean", + "default": true + } + } + }, + "underef": { + "type": "object", + "additionalProperties": false, + "properties": { + "skipRecvDeref" : { + "type": "boolean", + "default": true + } + } + }, + "unnamedResult": { + "type": "object", + "additionalProperties": false, + "properties": { + "checkExported" : { + "type": "boolean", + "default": false + } + } + } + } + }, + "disable-all": { + "type": "boolean", + "default": false + }, + "enable-all": { + "type": "boolean", + "default": false + } + } + }, + "gocycloSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum code complexity to report (we recommend 10-20).", + "type": "integer", + "default": 30 + } + } + }, + "godoclintSettings": { + "type": "object", + "properties": { + "default": { + "type": "string", + "enum": ["all", "basic", "none"], + "default": "basic", + "description": "Default set of rules to enable." + }, + "enable": { + "description": "List of rules to enable in addition to the default set.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "disable": { + "description": "List of rules to disable.", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/definitions/godoclint-rules" + } + }, + "options": { + "type": "object", + "description": "A map for setting individual rule options.", + "properties": { + "max-len": { + "type": "object", + "properties": { + "length": { + "type": "integer", + "description": "Maximum line length for godocs, not including the `//`, `/*` or `*/` tokens.", + "default": 77 + } + } + }, + "require-doc": { + "type": "object", + "properties": { + "ignore-exported": { + "type": "boolean", + "description": "Ignore exported (public) symbols when applying the `require-doc` rule.", + "default": false + }, + "ignore-unexported": { + "type": "boolean", + "description": "Ignore unexported (private) symbols when applying the `require-doc` rule.", + "default": true + } + } + }, + "start-with-name": { + "type": "object", + "properties": { + "include-unexported": { + "type": "boolean", + "description": "Include unexported symbols when applying the `start-with-name` rule.", + "default": false + } + } + } + } + } + } + }, + "godotSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "scope": { + "description": "Comments to be checked.", + "enum": ["declarations", "toplevel", "all", "noinline"], + "default": "declarations" + }, + "exclude": { + "description": "List of regexps for excluding particular comment lines from check.", + "type": "array", + "items": { + "type": "string" + } + }, + "period": { + "description": "Check that each sentence ends with a period.", + "type": "boolean", + "default": true + }, + "capital": { + "description": "Check that each sentence starts with a capital letter.", + "type": "boolean", + "default": false + }, + "check-all": { + "description": "DEPRECATED: Check all top-level comments, not only declarations.", + "type": "boolean", + "default": false + } + } + }, + "godoxSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "keywords": { + "description": "Report any comments starting with one of these keywords. This is useful for TODO or FIXME comments that might be left in the code accidentally and should be resolved before merging.", + "type": "array", + "items": { + "type": "string" + }, + "default": ["TODO", "BUG", "FIXME"] + } + } + }, + "gofmtSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "simplify": { + "description": "Simplify code.", + "type": "boolean", + "default": true + }, + "rewrite-rules": { + "description": "Apply the rewrite rules to the source before reformatting.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "replacement": { + "type": "string" + } + } + } + } + } + }, + "golinesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-len": { + "type": "integer", + "default": 100 + }, + "tab-len": { + "type": "integer", + "default": 4 + }, + "shorten-comments": { + "type": "boolean", + "default": false + }, + "reformat-tags": { + "type": "boolean", + "default": true + }, + "chain-split-dots": { + "type": "boolean", + "default": true + } + } + }, + "interfacebloatSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max": { + "description": "The maximum number of methods allowed for an interface.", + "type": "integer" + } + } + }, + "gofumptSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-rules": { + "description": "Choose whether or not to use the extra rules that are disabled by default.", + "type": "boolean", + "default": false + }, + "module-path": { + "description": " Module path which contains the source code being formatted.", + "type": "string" + } + } + }, + "goheaderSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "values": { + "type": "object", + "additionalProperties": false, + "properties": { + "const": { + "description": "Constants to use in the template.", + "type": "object", + "patternProperties": { + "^.+$": { + "description": "Value for the constant.", + "type": "string" + } + }, + "additionalProperties": false, + "examples": [ + { + "YEAR": "2030", + "COMPANY": "MY FUTURISTIC COMPANY" + } + ] + }, + "regexp": { + "description": "Regular expressions to use in your template.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "string" + } + }, + "examples": [ + { + "AUTHOR": ".*@mycompany\\.com" + } + ] + } + } + }, + "template": { + "description": "Template to put on top of every file.", + "type": "string", + "examples": [ + "{{ MY COMPANY }}\nSPDX-License-Identifier: Apache-2.0\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License." + ] + }, + "template-path": { + "description": "Path to the file containing the template source.", + "type": "string", + "examples": ["my_header_template.txt"] + } + }, + "oneOf": [ + { "required": ["template"] }, + { "required": ["template-path"] } + ] + }, + "goimportsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "local-prefixes": { + "description": "Put imports beginning with prefix after 3rd-party packages. It is a list of prefixes.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "gomoddirectivesSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "replace-local": { + "description": "Allow local `replace` directives.", + "type": "boolean", + "default": true + }, + "replace-allow-list": { + "description": "List of allowed `replace` directives.", + "type": "array", + "items": { + "type": "string" + } + }, + "retract-allow-no-explanation": { + "description": "Allow to not explain why the version has been retracted in the `retract` directives.", + "type": "boolean", + "default": false + }, + "exclude-forbidden": { + "description": "Forbid the use of the `exclude` directives.", + "type": "boolean", + "default": false + }, + "ignore-forbidden": { + "description": "Forbid the use of the `ignore` directives. (>= go1.25)", + "type": "boolean", + "default": false + }, + "toolchain-forbidden": { + "description": "Forbid the use of the `toolchain` directive.", + "type": "boolean", + "default": false + }, + "toolchain-pattern": { + "description": "Defines a pattern to validate `toolchain` directive.", + "type": "string" + }, + "tool-forbidden": { + "description": "Forbid the use of the `tool` directives.", + "type": "boolean", + "default": false + }, + "go-debug-forbidden": { + "description": "Forbid the use of the `godebug` directive.", + "type": "boolean", + "default": false + }, + "go-version-pattern": { + "description": "Defines a pattern to validate `go` minimum version directive.", + "type": "string", + "default": "" + }, + "check-module-path": { + "description": "Check the validity of the module path.", + "type": "boolean", + "default": false + } + } + }, + "gomodguardSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allowed": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of allowed modules.", + "type": "array", + "items": { + "type": "string", + "examples": ["gopkg.in/yaml.v2"] + } + }, + "domains": { + "description": "List of allowed module domains.", + "type": "array", + "items": { + "type": "string", + "examples": ["golang.org"] + } + } + } + }, + "blocked": { + "type": "object", + "additionalProperties": false, + "properties": { + "modules": { + "description": "List of blocked modules.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "recommendations": { + "description": "Recommended modules that should be used instead.", + "type": "array", + "items": { + "type": "string" + } + }, + "reason": { + "description": "Reason why the recommended module should be used.", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + }, + "versions": { + "description": "List of blocked module version constraints.", + "type": "array", + "items": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "description": "Version constraint.", + "type": "string" + }, + "reason": { + "description": "Reason why the version constraint exists.", + "type": "string" + } + }, + "required": ["reason"] + } + } + } + }, + "local-replace-directives": { + "description": "Raise lint issues if loading local path with replace directive", + "type": "boolean", + "default": true + } + } + } + } + }, + "gosecSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "includes": { + "type": "array", + "description": "To select a subset of rules to run", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "excludes": { + "type": "array", + "description": "To specify a set of rules to explicitly exclude", + "examples": [["G401"]], + "items": { + "$ref": "#/definitions/gosec-rules" + } + }, + "severity": { + "description": "Filter out the issues with a lower severity than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "confidence": { + "description": "Filter out the issues with a lower confidence than the given value", + "type": "string", + "enum": ["low", "medium", "high"], + "default": "low" + }, + "config": { + "description": "To specify the configuration of rules", + "type": "object" + }, + "concurrency": { + "description": "Concurrency value", + "type": "integer" + } + } + }, + "gosmopolitanSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-time-local": { + "description": "Allow and ignore `time.Local` usages.", + "type": "boolean", + "default": false + }, + "escape-hatches": { + "description": "List of fully qualified names in the `full/pkg/path.name` form, to act as \"i18n escape hatches\".", + "type": "array", + "items": { + "type": "string" + } + }, + "watch-for-scripts": { + "description": "List of Unicode scripts to watch for any usage in string literals.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "govetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "settings": { + "description": "Settings per analyzer. Map of analyzer name to specific settings.\nRun `go tool vet help` to find out more.", + "type": "object", + "propertyNames": { + "$ref": "#/definitions/govet-analyzers" + }, + "patternProperties": { + "^.*$": { + "description": "Run `go tool vet help ` to see all settings.", + "type": "object" + } + } + }, + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "disable": { + "description": "Disable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/govet-analyzers" + } + }, + "enable-all": { + "description": "Enable all analyzers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all analyzers.", + "type": "boolean", + "default": false + } + } + }, + "grouperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "const-require-single-const": { + "type": "boolean", + "default": false + }, + "const-require-grouping": { + "type": "boolean", + "default": false + }, + "import-require-single-import": { + "type": "boolean", + "default": false + }, + "import-require-grouping": { + "type": "boolean", + "default": false + }, + "type-require-single-type": { + "type": "boolean", + "default": false + }, + "type-require-grouping": { + "type": "boolean", + "default": false + }, + "var-require-single-var": { + "type": "boolean", + "default": false + }, + "var-require-grouping": { + "type": "boolean", + "default": false + } + } + }, + "ifaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "Enable analyzers by name.", + "type": "array", + "items": { + "$ref": "#/definitions/iface-analyzers" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "unused": { + "type": "object", + "additionalProperties": false, + "properties": { + "exclude": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "importasSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "no-unaliased": { + "description": "Do not allow unaliased imports of aliased packages.", + "type": "boolean", + "default": false + }, + "no-extra-aliases": { + "description": "Do not allow non-required aliases.", + "type": "boolean", + "default": false + }, + "alias": { + "description": "List of aliases", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "pkg": { + "description": "Package path e.g. knative.dev/serving/pkg/apis/autoscaling/v1alpha1", + "type": "string" + }, + "alias": { + "description": "Package alias e.g. autoscalingv1alpha1", + "type": "string" + } + }, + "required": ["pkg", "alias"] + } + } + } + }, + "inamedparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-single-param": { + "description": "Skips check for interface methods with only a single parameter.", + "type": "boolean", + "default": false + } + } + }, + "ineffassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-escaping-errors": { + "description": "Check escaping variables of type error, may cause false positives.", + "type": "boolean", + "default": false + } + } + }, + "iotamixingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-individual": { + "description": "Whether to report individual consts rather than just the const block.", + "type": "boolean", + "default": false + } + } + }, + "ireturnSettings": { + "type": "object", + "additionalProperties": false, + "description": "Use either `reject` or `allow` properties for interfaces matching.", + "properties": { + "allow": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + }, + "reject": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": ["anon", "error", "empty", "stdlib"] + } + ] + } + } + }, + "anyOf": [ + { + "not": { + "properties": { + "allow": { + "const": "reject" + } + } + }, + "required": ["allow"] + }, + { + "required": ["reject"] + } + ] + }, + "lllSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "tab-width": { + "description": "Width of \"\\t\" in spaces.", + "type": "integer", + "minimum": 0, + "default": 1 + }, + "line-length": { + "description": "Maximum allowed line length, lines longer will be reported.", + "type": "integer", + "minimum": 1, + "default": 120 + } + } + }, + "maintidxSettings": { + "description": "Maintainability index https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022", + "type": "object", + "additionalProperties": false, + "properties": { + "under": { + "description": "Minimum accatpable maintainability index level (see https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-maintainability-index-range-and-meaning?view=vs-2022)", + "type": "number", + "default": 20 + } + } + }, + "makezeroSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "always": { + "description": "Allow only slices initialized with a length of zero.", + "type": "boolean", + "default": false + } + } + }, + "loggercheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kitlog": { + "description": "Allow check for the github.com/go-kit/log library.", + "type": "boolean", + "default": true + }, + "klog": { + "description": "Allow check for the k8s.io/klog/v2 library.", + "type": "boolean", + "default": true + }, + "logr": { + "description": "Allow check for the github.com/go-logr/logr library.", + "type": "boolean", + "default": true + }, + "slog": { + "description": "Allow check for the log/slog library.", + "type": "boolean", + "default": true + }, + "zap": { + "description": "Allow check for the \"sugar logger\" from go.uber.org/zap library.", + "type": "boolean", + "default": true + }, + "require-string-key": { + "description": "Require all logging keys to be inlined constant strings.", + "type": "boolean", + "default": false + }, + "no-printf-like": { + "description": "Require printf-like format specifier (%s, %d for example) not present.", + "type": "boolean", + "default": false + }, + "rules": { + "description": "List of custom rules to check against, where each rule is a single logger pattern, useful for wrapped loggers.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "misspellSettings": { + "description": "Correct spellings using locale preferences for US or UK. Default is to use a neutral variety of English.", + "type": "object", + "additionalProperties": false, + "properties": { + "locale": { + "enum": ["US", "UK"] + }, + "ignore-rules": { + "description": "List of rules to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "mode": { + "description": "Mode of the analysis.", + "enum": ["restricted", "", "default"], + "default": "" + }, + "extra-words": { + "description": "Extra word corrections.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "correction": { + "type": "string" + }, + "typo": { + "type": "string" + } + } + } + } + } + }, + "musttagSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "functions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "arg-pos": { + "type": "integer" + } + } + } + } + } + }, + "nakedretSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-func-lines": { + "description": "Report if a function has more lines of code than this value and it has naked returns.", + "type": "integer", + "minimum": 0, + "default": 30 + } + } + }, + "nestifSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "min-complexity": { + "description": "Minimum complexity of \"if\" statements to report.", + "type": "integer", + "default": 5 + } + } + }, + "nilnilSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "only-two": { + "type": "boolean", + "description": "To check functions with only two return values.", + "default": true + }, + "detect-opposite": { + "type": "boolean", + "description": "In addition, detect opposite situation (simultaneous return of non-nil error and valid value).", + "default": false + }, + "checked-types": { + "type": "array", + "description": "List of return types to check.", + "items": { + "enum": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + }, + "default": ["chan", "func", "iface", "map", "ptr", "uintptr", "unsafeptr"] + } + } + }, + "nlreturnSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "block-size": { + "description": "set block size that is still ok", + "type": "number", + "default": 0, + "minimum": 0 + } + } + }, + "mndSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignored-files": { + "description": "List of file patterns to exclude from analysis.", + "examples": [["magic1_.*.go"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Comma-separated list of function patterns to exclude from the analysis.", + "examples": [["math.*", "http.StatusText", "make"]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-numbers": { + "description": "List of numbers to exclude from analysis.", + "examples": [["1000", "1234_567_890", "3.14159264"]], + "type": "array", + "items": { + "type": "string" + } + }, + "checks": { + "description": "The list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.", + "type": "array", + "items": { + "enum": [ + "argument", + "case", + "condition", + "operation", + "return", + "assign" + ] + } + } + } + }, + "modernizeSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable": { + "description": "List of analyzers to disable.", + "type": "array", + "items": { + "$ref": "#/definitions/modernize-analyzers" + } + } + } + }, + "nolintlintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-unused": { + "description": "Enable to ensure that nolint directives are all used.", + "type": "boolean", + "default": true + }, + "allow-no-explanation": { + "description": "Exclude these linters from requiring an explanation.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + }, + "default": [] + }, + "require-explanation": { + "description": "Enable to require an explanation of nonzero length after each nolint directive.", + "type": "boolean", + "default": false + }, + "require-specific": { + "description": "Enable to require nolint directives to mention the specific linter being suppressed.", + "type": "boolean", + "default": false + } + } + }, + "reassignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "recvcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "disable-builtin": { + "description": "Disables the built-in method exclusions.", + "type": "boolean", + "default": true + }, + "exclusions": { + "description": "User-defined method exclusions.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "nonamedreturnsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "report-error-in-defer": { + "description": "Report named error if it is assigned inside defer.", + "type": "boolean", + "default": false + } + } + }, + "paralleltestSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-missing": { + "description": "Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.", + "type": "boolean", + "default": false + }, + "ignore-missing-subtests": { + "description": "Ignore missing calls to `t.Parallel()` in subtests. Top-level tests are still required to have `t.Parallel`, but subtests are allowed to skip it.", + "type": "boolean", + "default": false + } + } + }, + "perfsprintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "integer-format": { + "description": "Enable/disable optimization of integer formatting.", + "type": "boolean", + "default": true + }, + "int-conversion": { + "description": "Optimizes even if it requires an int or uint type cast.", + "type": "boolean", + "default": true + }, + "error-format": { + "description": "Enable/disable optimization of error formatting.", + "type": "boolean", + "default": true + }, + "err-error": { + "description": "Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.", + "type": "boolean", + "default": false + }, + "errorf": { + "description": "Optimizes `fmt.Errorf`.", + "type": "boolean", + "default": true + }, + "string-format": { + "description": "Enable/disable optimization of string formatting.", + "type": "boolean", + "default": true + }, + "sprintf1": { + "description": "Optimizes `fmt.Sprintf` with only one argument.", + "type": "boolean", + "default": true + }, + "strconcat": { + "description": "Optimizes into strings concatenation.", + "type": "boolean", + "default": true + }, + "bool-format": { + "description": "Enable/disable optimization of bool formatting.", + "type": "boolean", + "default": true + }, + "hex-format": { + "description": "Enable/disable optimization of hex formatting.", + "type": "boolean", + "default": true + }, + "concat-loop": { + "description": "Enable/disable optimization of concat loop.", + "type": "boolean", + "default": true + }, + "loop-other-ops": { + "description": "Optimization of `concat-loop` even with other operations.", + "type": "boolean", + "default": false + } + } + }, + "preallocSettings": { + "description": "We do not recommend using this linter before doing performance profiling.\nFor most programs usage of `prealloc` will be premature optimization.", + "type": "object", + "additionalProperties": false, + "properties": { + "simple": { + "description": "Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.", + "type": "boolean", + "default": true + }, + "range-loops": { + "description": "Report preallocation suggestions on range loops.", + "type": "boolean", + "default": true + }, + "for-loops": { + "description": "Report preallocation suggestions on for loops.", + "type": "boolean", + "default": false + } + } + }, + "predeclaredSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore": { + "description": "List of predeclared identifiers to not report on.", + "type": "array", + "items": { + "type": "string" + } + }, + "qualified-name": { + "description": "Include method names and field names in checks.", + "type": "boolean", + "default": false + } + } + }, + "promlinterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "strict": {}, + "disabled-linters": { + "type": "array", + "items": { + "enum": [ + "Help", + "MetricUnits", + "Counter", + "HistogramSummaryReserved", + "MetricTypeInName", + "ReservedChars", + "CamelCase", + "UnitAbbreviations" + ] + } + } + } + }, + "protogetterSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-generated-by": { + "type": "array", + "items": { + "type": "string", + "examples": ["protoc-gen-go-my-own-generator"] + } + }, + "skip-files": { + "type": "array", + "items": { + "type": "string", + "examples": ["*.pb.go"] + } + }, + "skip-any-generated": { + "description": "Skip any generated files from the checking.", + "type": "boolean", + "default": false + }, + "replace-first-arg-in-append": { + "description": "Skip first argument of append function.", + "type": "boolean", + "default": false + } + } + }, + "reviveSettings": { + "type": "object", + "additionalProperties": false, + "examples": [ + { + "ignore-generated-header": true, + "severity": "warning", + "rules": [ + { + "name": "indent-error-flow", + "severity": "warning" + }, + { + "name": "add-constant", + "severity": "warning", + "arguments": [ + { + "maxLitCount": "3", + "allowStrs": "\"\"", + "allowInts": "0,1,2", + "allowFloats": "0.0,0.,1.0,1.,2.0,2." + } + ] + } + ] + } + ], + "properties": { + "max-open-files": { + "type": "integer" + }, + "confidence": { + "type": "number" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "enable-all-rules": { + "type": "boolean", + "default": false + }, + "enable-default-rules": { + "type": "boolean", + "default": false + }, + "directives": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["specify-disable-reason"] + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": { + "$ref": "#/definitions/revive-rules", + "title": "The rule name" + }, + "disabled": { + "type": "boolean" + }, + "severity": { + "type": "string", + "enum": ["warning", "error"] + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + } + }, + "arguments": { + "type": "array" + } + } + } + } + } + }, + "rowserrcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "description": "", + "type": "string", + "examples": ["github.com/jmoiron/sqlx"] + } + } + } + }, + "sloglintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "kv-only": { + "description": "Enforce using key-value pairs only (incompatible with attr-only).", + "type": "boolean", + "default": false + }, + "no-global": { + "description": "Enforce not using global loggers.", + "enum": ["", "all", "default"], + "default": "" + }, + "no-mixed-args": { + "description": "Enforce not mixing key-value pairs and attributes.", + "type": "boolean", + "default": true + }, + "context": { + "description": "Enforce using methods that accept a context.", + "enum": ["", "all", "scope"], + "default": "" + }, + "static-msg": { + "description": "Enforce using static values for log messages.", + "type": "boolean", + "default": false + }, + "msg-style": { + "description": "Enforce message style.", + "enum": ["", "lowercased", "capitalized"], + "default": "" + }, + "key-naming-case": { + "description": "Enforce a single key naming convention.", + "enum": ["snake", "kebab", "camel", "pascal"] + }, + "attr-only": { + "description": "Enforce using attributes only (incompatible with kv-only).", + "type": "boolean", + "default": false + }, + "no-raw-keys": { + "description": "Enforce using constants instead of raw keys.", + "type": "boolean", + "default": false + }, + "forbidden-keys": { + "description": "Enforce not using specific keys.", + "type": "array", + "items": { + "type": "string" + } + }, + "args-on-sep-lines": { + "description": "Enforce putting arguments on separate lines.", + "type": "boolean", + "default": false + } + } + }, + "spancheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "description": "Checks to enable.", + "type": "array", + "items": { + "enum": ["end", "record-error", "set-status"] + } + }, + "ignore-check-signatures": { + "description": "A list of regexes for function signatures that silence `record-error` and `set-status` reports if found in the call path to a returned error.", + "type": "array", + "items": { + "type": "string" + } + }, + "extra-start-span-signatures": { + "description": "A list of regexes for additional function signatures that create spans.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "staticcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "checks": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/staticcheck-checks" + }, + { + "type": "string" + } + ] + } + }, + "dot-import-whitelist": { + "description": "By default, ST1001 forbids all uses of dot imports in non-test packages. This setting allows setting a whitelist of import paths that can be dot-imported anywhere.", + "type": "array", + "items": { + "type": "string" + } + }, + "http-status-code-whitelist": { + "description": "ST1013 recommends using constants from the net/http package instead of hard-coding numeric HTTP status codes. This setting specifies a list of numeric status codes that this check does not complain about.", + "default": ["200", "400", "404", "500"], + "type": "array", + "items": { + "enum": [ + "100", + "101", + "102", + "103", + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + "300", + "301", + "302", + "303", + "304", + "305", + "306", + "307", + "308", + "400", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "409", + "410", + "411", + "412", + "413", + "414", + "415", + "416", + "417", + "418", + "421", + "422", + "423", + "424", + "425", + "426", + "428", + "429", + "431", + "451", + "500", + "501", + "502", + "503", + "504", + "505", + "506", + "507", + "508", + "510", + "511" + ] + } + }, + "initialisms": { + "description": "ST1003 check, among other things, for the correct capitalization of initialisms. The set of known initialisms can be configured with this option.", + "type": "array", + "items": { + "type": "string", + "default": [ + "ACL", + "API", + "ASCII", + "CPU", + "CSS", + "DNS", + "EOF", + "GUID", + "HTML", + "HTTP", + "HTTPS", + "ID", + "IP", + "JSON", + "QPS", + "RAM", + "RPC", + "SLA", + "SMTP", + "SQL", + "SSH", + "TCP", + "TLS", + "TTL", + "UDP", + "UI", + "GID", + "UID", + "UUID", + "URI", + "URL", + "UTF8", + "VM", + "XML", + "XMPP", + "XSRF", + "XSS", + "SIP", + "RTP", + "AMQP", + "DB", + "TS" + ] + } + } + } + }, + "tagalignSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "align": { + "description": "Align and sort can be used together or separately.", + "type": "boolean", + "default": true + }, + "sort": { + "description": "Whether enable tags sort.", + "type": "boolean", + "default": true + }, + "order": { + "description": "Specify the order of tags, the other tags will be sorted by name.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [ + [ + "json", + "yaml", + "yml", + "toml", + "mapstructure", + "binding", + "validate" + ] + ] + }, + "strict": { + "description": "Whether enable strict style.", + "type": "boolean", + "default": false + } + } + }, + "tagliatelleSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "case": { + "type": "object", + "additionalProperties": false, + "properties": { + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + }, + "overrides": { + "description": "Overrides the default/root configuration.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["pkg"], + "properties": { + "pkg": { + "description": "A package path.", + "type": "string" + }, + "use-field-name": { + "description": "Use the struct field name to check the name of the struct tag.", + "type": "boolean", + "default": false + }, + "ignored-fields": { + "description": "The field names to ignore.", + "type": "array", + "items": { + "type": "string", + "examples": ["example"] + } + }, + "ignore": { + "description": "Ignore the package (takes precedence over all other configurations).", + "type": "boolean", + "default": false + }, + "rules": { + "type": "object", + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/tagliatelle-cases" + } + } + }, + "extended-rules": { + "description": "Defines the association between tag name and case.", + "type": "object", + "patternProperties": { + "^.+$": { + "type": "object", + "additionalProperties": false, + "required": ["case"], + "properties": { + "case": { + "$ref": "#/definitions/tagliatelle-cases" + }, + "extra-initialisms": { + "type": "boolean", + "default": false + }, + "initialism-overrides": { + "type": "object", + "patternProperties": { + "^.+$": { + "type": "boolean", + "default": false + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "testifylintSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable-all": { + "description": "Enable all checkers.", + "type": "boolean", + "default": false + }, + "disable-all": { + "description": "Disable all checkers.", + "type": "boolean", + "default": false + }, + "enable": { + "description": "Enable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ] + }, + "default": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "useless-assert" + ] + }, + "disable": { + "description": "Disable specific checkers.", + "type": "array", + "items": { + "enum": [ + "blank-import", + "bool-compare", + "compares", + "contains", + "empty", + "encoded-compare", + "equal-values", + "error-is-as", + "error-nil", + "expected-actual", + "float-compare", + "formatter", + "go-require", + "len", + "negative-positive", + "nil-compare", + "regexp", + "require-error", + "suite-broken-parallel", + "suite-dont-use-pkg", + "suite-extra-assert-call", + "suite-method-signature", + "suite-subtest-run", + "suite-thelper", + "useless-assert" + ], + "default": [ + "suite-thelper" + ] + } + }, + "bool-compare": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-custom-types": { + "description": "To ignore user defined types (over builtin bool).", + "type": "boolean", + "default": false + } + } + }, + "expected-actual": { + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "description": "Regexp for expected variable name.", + "type": "string", + "default": "(^(exp(ected)?|want(ed)?)([A-Z]\\w*)?$)|(^(\\w*[a-z])?(Exp(ected)?|Want(ed)?)$)" + } + } + }, + "formatter": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-format-string": { + "description": "To enable go vet's printf checks.", + "type": "boolean", + "default": true + }, + "require-f-funcs": { + "description": "To require f-assertions (e.g. assert.Equalf) if format string is used, even if there are no variable-length variables.", + "type": "boolean", + "default": false + }, + "require-string-msg": { + "description": "To require that the first element of msgAndArgs (msg) has a string type.", + "type": "boolean", + "default": true + } + } + }, + "go-require": { + "type": "object", + "additionalProperties": false, + "properties": { + "ignore-http-handlers": { + "description": "To ignore HTTP handlers (like http.HandlerFunc).", + "type": "boolean", + "default": false + } + } + }, + "require-error": { + "type": "object", + "additionalProperties": false, + "properties": { + "fn-pattern": { + "description": "Regexp for assertions to analyze. If defined, then only matched error assertions will be reported.", + "type": "string", + "default": "" + } + } + }, + "suite-extra-assert-call": { + "type": "object", + "additionalProperties": false, + "properties": { + "mode": { + "description": "To require or remove extra Assert() call?", + "type": "string", + "enum": ["remove", "require"], + "default": "remove" + } + } + } + } + }, + "testpackageSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "skip-regexp": { + "description": "Files with names matching this regular expression are skipped.", + "type": "string", + "examples": ["(export|internal)_test\\.go"] + }, + "allow-packages": { + "description": "List of packages that don't end with _test that tests are allowed to be in.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "examples": ["example"] + } + } + } + }, + "thelperSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "test": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `t.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.T is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.T param has t name.", + "default": true, + "type": "boolean" + } + } + }, + "benchmark": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `b.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.B is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.B param has b name.", + "default": true, + "type": "boolean" + } + } + }, + "tb": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `tb.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.TB is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.TB param has tb name.", + "default": true, + "type": "boolean" + } + } + }, + "fuzz": { + "type": "object", + "additionalProperties": false, + "properties": { + "begin": { + "description": "Check if `f.Helper()` begins helper function.", + "default": true, + "type": "boolean" + }, + "first": { + "description": "Check if *testing.F is first param of helper function.", + "default": true, + "type": "boolean" + }, + "name": { + "description": "Check if *testing.F param has f name.", + "default": true, + "type": "boolean" + } + } + } + } + }, + "usestdlibvarsSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "http-method": { + "description": "Suggest the use of http.MethodXX.", + "type": "boolean", + "default": true + }, + "http-status-code": { + "description": "Suggest the use of http.StatusXX.", + "type": "boolean", + "default": true + }, + "time-weekday": { + "description": "Suggest the use of time.Weekday.String().", + "type": "boolean", + "default": false + }, + "time-month": { + "description": "Suggest the use of time.Month.String().", + "type": "boolean", + "default": false + }, + "time-layout": { + "description": "Suggest the use of time.Layout.", + "type": "boolean", + "default": false + }, + "time-date-month": { + "description": "Suggest the use of time.Month in time.Date.", + "type": "boolean", + "default": false + }, + "crypto-hash": { + "description": "Suggest the use of crypto.Hash.String().", + "type": "boolean", + "default": false + }, + "default-rpc-path": { + "description": "Suggest the use of rpc.DefaultXXPath.", + "type": "boolean", + "default": false + }, + "sql-isolation-level": { + "description": "Suggest the use of sql.LevelXX.String().", + "type": "boolean", + "default": false + }, + "tls-signature-scheme": { + "description": "Suggest the use of tls.SignatureScheme.String().", + "type": "boolean", + "default": false + }, + "constant-kind": { + "description": "Suggest the use of constant.Kind.String().", + "type": "boolean", + "default": false + } + } + }, + "usetestingSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "context-background": { + "type": "boolean", + "default": false + }, + "context-todo": { + "type": "boolean", + "default": false + }, + "os-chdir": { + "type": "boolean", + "default": true + }, + "os-mkdir-temp": { + "type": "boolean", + "default": true + }, + "os-setenv": { + "type": "boolean", + "default": true + }, + "os-create-temp": { + "type": "boolean", + "default": true + }, + "os-temp-dir": { + "type": "boolean", + "default": false + } + } + }, + "unconvertSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "fast-math": { + "type": "boolean", + "default": false + }, + "safe": { + "type": "boolean", + "default": false + } + } + }, + "unparamSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-exported": { + "description": "Inspect exported functions. Set to true if no external program/library imports your code.\n\nWARNING: if you enable this setting, unparam will report a lot of false-positives in text editors:\nif it's called for subdir of a project it can't find external interfaces. All text editor integrations\nwith golangci-lint call it on a directory with the changed file.", + "type": "boolean", + "default": false + } + } + }, + "unqueryvetSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-sql-builders": { + "description": "Enable SQL builder checking.", + "type": "boolean", + "default": true + }, + "check-aliased-wildcard": { + "description": "Enable aliased wildcard detection like SELECT t.*.", + "type": "boolean", + "default": true + }, + "check-string-concat": { + "description": "Enable string concatenation analysis.", + "type": "boolean", + "default": true + }, + "check-format-strings": { + "description": "Enable format string analysis like fmt.Sprintf.", + "type": "boolean", + "default": true + }, + "check-string-builder": { + "description": "Enable strings.Builder analysis.", + "type": "boolean", + "default": true + }, + "check-subqueries": { + "description": "Enable subquery analysis.", + "type": "boolean", + "default": true + }, + "check-n1": { + "type": "boolean", + "default": false + }, + "check-sql-injection": { + "type": "boolean", + "default": false + }, + "check-tx-leaks": { + "type": "boolean", + "default": false + }, + "allowed-patterns": { + "description": "Regex patterns for acceptable SELECT * usage.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "Allow is a list of SQL patterns to allow (whitelist).", + "type": "array", + "items": { + "type": "string" + } + }, + "ignored-functions": { + "description": "Functions to ignore.", + "type": "array", + "items": { + "type": "string" + } + }, + "sql-builders": { + "type": "object", + "additionalProperties": false, + "properties": { + "squirrel": { + "type": "boolean", + "default": true + }, + "gorm": { + "type": "boolean", + "default": true + }, + "sqlx": { + "type": "boolean", + "default": true + }, + "ent": { + "type": "boolean", + "default": true + }, + "pgx": { + "type": "boolean", + "default": true + }, + "bun": { + "type": "boolean", + "default": true + }, + "sqlboiler": { + "type": "boolean", + "default": true + }, + "jet": { + "type": "boolean", + "default": true + } + } + }, + "custom-rules": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "pattern": { + "type": "string" + }, + "patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "when": { + "type": "string" + }, + "message": { + "type": "string" + }, + "action": { + "type": "string" + } + } + } + } + } + }, + "unusedSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "field-writes-are-uses": { + "description": "", + "type": "boolean", + "default": true + }, + "post-statements-are-reads": { + "description": "", + "type": "boolean", + "default": false + }, + "exported-fields-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "parameters-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "local-variables-are-used": { + "description": "", + "type": "boolean", + "default": true + }, + "generated-is-used": { + "description": "", + "type": "boolean", + "default": true + } + } + }, + "varnamelenSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-distance": { + "description": "Variables used in at most this N-many lines will be ignored.", + "type": "integer", + "default": 5 + }, + "min-name-length": { + "description": "The minimum length of a variable's name that is considered `long`.", + "type": "integer", + "default": 3 + }, + "check-receiver": { + "description": "Check method receiver names.", + "default": false, + "type": "boolean" + }, + "check-return": { + "description": "Check named return values.", + "default": false, + "type": "boolean" + }, + "check-type-param": { + "description": "Check type parameters.", + "default": false, + "type": "boolean" + }, + "ignore-type-assert-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a type assertion", + "default": false, + "type": "boolean" + }, + "ignore-map-index-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a map index.", + "default": false, + "type": "boolean" + }, + "ignore-chan-recv-ok": { + "description": "Ignore `ok` variables that hold the bool return value of a channel receive.", + "default": false, + "type": "boolean" + }, + "ignore-names": { + "description": "Optional list of variable names that should be ignored completely.", + "default": [[]], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-decls": { + "description": "Optional list of variable declarations that should be ignored completely.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["c echo.Context", "t testing.T", "f *foo.Bar", "const C"] + ] + } + } + }, + "whitespaceSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "multi-if": { + "description": "Enforces newlines (or comments) after every multi-line if statement", + "type": "boolean", + "default": false + }, + "multi-func": { + "description": "Enforces newlines (or comments) after every multi-line function signature", + "type": "boolean", + "default": false + } + } + }, + "wrapcheckSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "extra-ignore-sigs": { + "description": "An array of strings specifying additional substrings of signatures to ignore.", + "default": [ + ".CustomError(", + ".SpecificWrap(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sigs": { + "description": "An array of strings which specify substrings of signatures to ignore.", + "default": [ + ".Errorf(", + "errors.New(", + "errors.Unwrap(", + ".Wrap(", + ".Wrapf(", + ".WithMessage(", + ".WithMessagef(", + ".WithStack(" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-sig-regexps": { + "description": "An array of strings which specify regular expressions of signatures to ignore.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-package-globs": { + "description": "An array of glob patterns which, if any match the package of the function returning the error, will skip wrapcheck analysis for this error.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "ignore-interface-regexps": { + "description": "An array of glob patterns which, if matched to an underlying interface name, will ignore unwrapped errors returned from a function whose call is defined on the given interface.", + "default": [""], + "type": "array", + "items": { + "type": "string" + } + }, + "report-internal-errors": { + "description": "Determines whether wrapcheck should report errors returned from inside the package.", + "type": "boolean", + "default": false + } + } + }, + "wslSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-assign-and-anything": { + "description": "Controls if you may cuddle assignments and anything without needing an empty line between them.", + "type": "boolean", + "default": false + }, + "allow-assign-and-call": { + "description": "Allow calls and assignments to be cuddled as long as the lines have any matching variables, fields or types.", + "type": "boolean", + "default": true + }, + "allow-cuddle-declarations": { + "description": "Allow declarations (var) to be cuddled.", + "type": "boolean", + "default": false + }, + "allow-cuddle-with-calls": { + "description": "A list of call idents that everything can be cuddled with.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-with-rhs": { + "description": "AllowCuddleWithRHS is a list of right hand side variables that is allowed to be cuddled with anything.", + "type": "array", + "items": { + "type": "string" + } + }, + "allow-cuddle-used-in-block": { + "description": "Allow cuddling with any block as long as the variable is used somewhere in the block", + "type": "boolean", + "default": false + }, + "allow-multiline-assign": { + "description": "Allow multiline assignments to be cuddled.", + "type": "boolean", + "default": true + }, + "allow-separated-leading-comment": { + "description": "Allow leading comments to be separated with empty lines.", + "type": "boolean", + "default": false + }, + "allow-trailing-comment": { + "description": "Allow trailing comments in ending of blocks.", + "type": "boolean", + "default": false + }, + "error-variable-names": { + "description": "When force-err-cuddling is enabled this is a list of names used for error variables to check for in the conditional.", + "type": "array", + "items": { + "type": "string" + } + }, + "force-case-trailing-whitespace": { + "description": "Force newlines in end of case at this limit (0 = never).", + "type": "integer", + "minimum": 0, + "default": 0 + }, + "force-err-cuddling": { + "description": "Causes an error when an If statement that checks an error variable doesn't cuddle with the assignment of that variable.", + "type": "boolean", + "default": false + }, + "force-short-decl-cuddling": { + "description": "Causes an error if a short declaration (:=) cuddles with anything other than another short declaration.", + "type": "boolean", + "default": false + }, + "strict-append": { + "description": "If true, append is only allowed to be cuddled if appending value is matching variables, fields or types on line above.", + "type": "boolean", + "default": true + } + } + }, + "wslSettingsV5": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow-first-in-block": { + "type": "boolean", + "default": true + }, + "allow-whole-block": { + "type": "boolean", + "default": false + }, + "branch-max-lines": { + "type": "integer", + "default": 2 + }, + "case-max-lines": { + "type": "integer", + "default": 0 + }, + "default": { + "enum": ["all", "none", "default", ""], + "default": "default" + }, + "enable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + }, + "disable": { + "type": "array", + "items": { + "$ref": "#/definitions/wsl-checks" + } + } + } + }, + "copyloopvarSettings": { + "type": "object", + "additionalProperties": false, + "properties": { + "check-alias": { + "type": "boolean", + "default": false + } + } + }, + "customSettings": { + "description": "The custom section can be used to define linter plugins to be loaded at runtime. See README of golangci-lint for more information.\nEach custom linter should have a unique name.", + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The plugin type.", + "enum": ["module", "goplugin"], + "default": "goplugin" + }, + "path": { + "description": "The path to the plugin *.so. Can be absolute or local.", + "type": "string", + "examples": ["/path/to/example.so"] + }, + "description": { + "description": "The description of the linter, for documentation purposes only.", + "type": "string" + }, + "original-url": { + "description": "Intended to point to the repo location of the linter, for documentation purposes only.", + "type": "string" + }, + "settings": { + "description": "Plugins settings/configuration. Only work with plugin based on `linterdb.PluginConstructor`.", + "type": "object" + } + }, + "oneOf": [ + { + "properties": { + "type": {"enum": ["module"] } + }, + "required": ["type"] + }, + { + "required": ["path"] + } + ] + } + } + } + } + } + }, + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { + "type": "string", + "default": "2" + }, + "run": { + "description": "Options for analysis running,", + "type": "object", + "additionalProperties": false, + "properties": { + "concurrency": { + "description": "Number of concurrent runners. Defaults to the number of available CPU cores.", + "type": "integer", + "minimum": 0, + "examples": [4] + }, + "timeout": { + "description": "Timeout for the analysis.", + "type": "string", + "pattern": "^((\\d+h)?(\\d+m)?(\\d+(?:\\.\\d)?s)?|0)$", + "default": "1m", + "examples": ["30s", "5m", "5m30s"] + }, + "issues-exit-code": { + "description": "Exit code when at least one issue was found.", + "type": "integer", + "default": 1 + }, + "tests": { + "description": "Enable inclusion of test files.", + "type": "boolean", + "default": true + }, + "build-tags": { + "description": "List of build tags to pass to all linters.", + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "examples": [["mytag"]] + }, + "modules-download-mode": { + "description": "Option to pass to \"go list -mod={option}\".\nSee \"go help modules\" for more information.", + "enum": ["mod", "readonly", "vendor"] + }, + "enable-build-vcs": { + "type": "boolean", + "default": false + }, + "allow-parallel-runners": { + "description": "Allow multiple parallel golangci-lint instances running. If disabled, golangci-lint acquires file lock on start.", + "type": "boolean", + "default": false + }, + "allow-serial-runners": { + "description": "Allow multiple golangci-lint instances running, but serialize them around a lock.", + "type": "boolean", + "default": false + }, + "go": { + "description": "Targeted Go version.", + "type": "string", + "default": "1.17" + }, + "relative-path-mode": { + "description": "The mode used to evaluate relative paths.", + "type": "string", + "$ref": "#/definitions/relative-path-modes", + "default": "wd" + } + } + }, + "output": { + "description": "Output configuration options.", + "type": "object", + "additionalProperties": false, + "properties": { + "formats": { + "description": "Output formats to use.", + "type": "object", + "additionalProperties": false, + "properties": { + "text": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "print-issued-lines": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "json": { + "$ref": "#/definitions/simple-format" + }, + "tab": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "print-linter-name": { + "type": "boolean", + "default": true + }, + "colors": { + "type": "boolean", + "default": true + } + } + }, + "html": { + "$ref": "#/definitions/simple-format" + }, + "checkstyle": { + "$ref": "#/definitions/simple-format" + }, + "code-climate": { + "$ref": "#/definitions/simple-format" + }, + "junit-xml": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "$ref": "#/definitions/formats-path", + "default": "stdout" + }, + "extended": { + "type": "boolean", + "default": true + } + } + }, + "teamcity": { + "$ref": "#/definitions/simple-format" + }, + "sarif": { + "$ref": "#/definitions/simple-format" + } + } + }, + "path-mode": { + "type": "string", + "default": "", + "examples": ["abs"] + }, + "path-prefix": { + "description": "Add a prefix to the output file references.", + "type": "string", + "default": "" + }, + "show-stats": { + "description": "Show statistics per linter.", + "type": "boolean", + "default": true + }, + "sort-order": { + "type": "array", + "items": { + "enum": ["linter", "severity", "file"] + } + } + } + }, + "linters": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "enum": [ + "standard", + "all", + "none", + "fast" + ] + }, + "enable": { + "description": "List of enabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "disable": { + "description": "List of disabled linters.", + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "settings": { + "description": "All available settings of specific linters.", + "type": "object", + "additionalProperties": false, + "properties": { + "dupword": { + "$ref": "#/definitions/settings/definitions/dupwordSettings" + }, + "asasalint": { + "$ref": "#/definitions/settings/definitions/asasalintSettings" + }, + "bidichk": { + "$ref": "#/definitions/settings/definitions/bidichkSettings" + }, + "cyclop": { + "$ref": "#/definitions/settings/definitions/cyclopSettings" + }, + "decorder": { + "$ref": "#/definitions/settings/definitions/decorderSettings" + }, + "depguard":{ + "$ref": "#/definitions/settings/definitions/depguardSettings" + }, + "dogsled": { + "$ref": "#/definitions/settings/definitions/dogsledSettings" + }, + "dupl": { + "$ref": "#/definitions/settings/definitions/duplSettings" + }, + "embeddedstructfieldcheck": { + "$ref": "#/definitions/settings/definitions/embeddedstructfieldcheckSettings" + }, + "errcheck": { + "$ref": "#/definitions/settings/definitions/errcheckSettings" + }, + "errchkjson": { + "$ref": "#/definitions/settings/definitions/errchkjsonSettings" + }, + "errorlint": { + "$ref": "#/definitions/settings/definitions/errorlintSettings" + }, + "exhaustive": { + "$ref": "#/definitions/settings/definitions/exhaustiveSettings" + }, + "exhaustruct": { + "$ref": "#/definitions/settings/definitions/exhaustructSettings" + }, + "fatcontext": { + "$ref": "#/definitions/settings/definitions/fatcontextSettings" + }, + "forbidigo": { + "$ref": "#/definitions/settings/definitions/forbidigoSettings" + }, + "funcorder": { + "$ref": "#/definitions/settings/definitions/funcorderSettings" + }, + "funlen": { + "$ref": "#/definitions/settings/definitions/funlenSettings" + }, + "ginkgolinter": { + "$ref": "#/definitions/settings/definitions/ginkgolinterSettings" + }, + "gochecksumtype": { + "$ref": "#/definitions/settings/definitions/gochecksumtypeSettings" + }, + "gocognit": { + "$ref": "#/definitions/settings/definitions/gocognitSettings" + }, + "goconst": { + "$ref": "#/definitions/settings/definitions/goconstSettings" + }, + "gocritic": { + "$ref": "#/definitions/settings/definitions/gocriticSettings" + }, + "gocyclo": { + "$ref": "#/definitions/settings/definitions/gocycloSettings" + }, + "godoclint": { + "$ref": "#/definitions/settings/definitions/godoclintSettings" + }, + "godot": { + "$ref": "#/definitions/settings/definitions/godotSettings" + }, + "godox": { + "$ref": "#/definitions/settings/definitions/godoxSettings" + }, + "interfacebloat":{ + "$ref": "#/definitions/settings/definitions/interfacebloatSettings" + }, + "goheader": { + "$ref": "#/definitions/settings/definitions/goheaderSettings" + }, + "gomoddirectives": { + "$ref": "#/definitions/settings/definitions/gomoddirectivesSettings" + }, + "gomodguard": { + "$ref": "#/definitions/settings/definitions/gomodguardSettings" + }, + "gosec": { + "$ref": "#/definitions/settings/definitions/gosecSettings" + }, + "gosmopolitan": { + "$ref": "#/definitions/settings/definitions/gosmopolitanSettings" + }, + "govet": { + "$ref": "#/definitions/settings/definitions/govetSettings" + }, + "grouper": { + "$ref": "#/definitions/settings/definitions/grouperSettings" + }, + "iface": { + "$ref": "#/definitions/settings/definitions/ifaceSettings" + }, + "importas": { + "$ref": "#/definitions/settings/definitions/importasSettings" + }, + "inamedparam": { + "$ref": "#/definitions/settings/definitions/inamedparamSettings" + }, + "ineffassign": { + "$ref": "#/definitions/settings/definitions/ineffassignSettings" + }, + "iotamixing": { + "$ref": "#/definitions/settings/definitions/iotamixingSettings" + }, + "ireturn": { + "$ref": "#/definitions/settings/definitions/ireturnSettings" + }, + "lll": { + "$ref": "#/definitions/settings/definitions/lllSettings" + }, + "maintidx": { + "$ref": "#/definitions/settings/definitions/maintidxSettings" + }, + "makezero":{ + "$ref": "#/definitions/settings/definitions/makezeroSettings" + }, + "loggercheck": { + "$ref": "#/definitions/settings/definitions/loggercheckSettings" + }, + "misspell": { + "$ref": "#/definitions/settings/definitions/misspellSettings" + }, + "musttag": { + "$ref": "#/definitions/settings/definitions/musttagSettings" + }, + "nakedret": { + "$ref": "#/definitions/settings/definitions/nakedretSettings" + }, + "nestif": { + "$ref": "#/definitions/settings/definitions/nestifSettings" + }, + "nilnil": { + "$ref": "#/definitions/settings/definitions/nilnilSettings" + }, + "nlreturn": { + "$ref": "#/definitions/settings/definitions/nlreturnSettings" + }, + "mnd": { + "$ref": "#/definitions/settings/definitions/mndSettings" + }, + "modernize": { + "$ref": "#/definitions/settings/definitions/modernizeSettings" + }, + "nolintlint":{ + "$ref": "#/definitions/settings/definitions/nolintlintSettings" + }, + "reassign": { + "$ref": "#/definitions/settings/definitions/reassignSettings" + }, + "recvcheck": { + "$ref": "#/definitions/settings/definitions/recvcheckSettings" + }, + "nonamedreturns": { + "$ref": "#/definitions/settings/definitions/nonamedreturnsSettings" + }, + "paralleltest": { + "$ref": "#/definitions/settings/definitions/paralleltestSettings" + }, + "perfsprint": { + "$ref": "#/definitions/settings/definitions/perfsprintSettings" + }, + "prealloc": { + "$ref": "#/definitions/settings/definitions/preallocSettings" + }, + "predeclared": { + "$ref": "#/definitions/settings/definitions/predeclaredSettings" + }, + "promlinter": { + "$ref": "#/definitions/settings/definitions/promlinterSettings" + }, + "protogetter": { + "$ref": "#/definitions/settings/definitions/protogetterSettings" + }, + "revive": { + "$ref": "#/definitions/settings/definitions/reviveSettings" + }, + "rowserrcheck": { + "$ref": "#/definitions/settings/definitions/rowserrcheckSettings" + }, + "sloglint": { + "$ref": "#/definitions/settings/definitions/sloglintSettings" + }, + "spancheck": { + "$ref": "#/definitions/settings/definitions/spancheckSettings" + }, + "staticcheck":{ + "$ref": "#/definitions/settings/definitions/staticcheckSettings" + }, + "tagalign": { + "$ref": "#/definitions/settings/definitions/tagalignSettings" + }, + "tagliatelle": { + "$ref": "#/definitions/settings/definitions/tagliatelleSettings" + }, + "testifylint": { + "$ref": "#/definitions/settings/definitions/testifylintSettings" + }, + "testpackage": { + "$ref": "#/definitions/settings/definitions/testpackageSettings" + }, + "thelper": { + "$ref": "#/definitions/settings/definitions/thelperSettings" + }, + "usestdlibvars": { + "$ref": "#/definitions/settings/definitions/usestdlibvarsSettings" + }, + "usetesting": { + "$ref": "#/definitions/settings/definitions/usetestingSettings" + }, + "unconvert": { + "$ref": "#/definitions/settings/definitions/unconvertSettings" + }, + "unparam": { + "$ref": "#/definitions/settings/definitions/unparamSettings" + }, + "unqueryvet": { + "$ref": "#/definitions/settings/definitions/unqueryvetSettings" + }, + "unused": { + "$ref": "#/definitions/settings/definitions/unusedSettings" + }, + "varnamelen": { + "$ref": "#/definitions/settings/definitions/varnamelenSettings" + }, + "whitespace": { + "$ref": "#/definitions/settings/definitions/whitespaceSettings" + }, + "wrapcheck": { + "$ref": "#/definitions/settings/definitions/wrapcheckSettings" + }, + "wsl": { + "$ref": "#/definitions/settings/definitions/wslSettings" + }, + "wsl_v5": { + "$ref": "#/definitions/settings/definitions/wslSettingsV5" + }, + "copyloopvar": { + "$ref": "#/definitions/settings/definitions/copyloopvarSettings" + }, + "custom":{ + "$ref": "#/definitions/settings/definitions/customSettings" + } + } + }, + "exclusions":{ + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "warn-unused": { + "type": "boolean", + "default": false + }, + "presets": { + "type": "array", + "items": { + "enum": [ + "comments", + "std-error-handling", + "common-false-positives", + "legacy" + ] + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths-except": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "formatters": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable": { + "description": "List of enabled formatters.", + "type": "array", + "items": { + "$ref": "#/definitions/formatter-names" + } + }, + "settings": { + "type": "object", + "additionalProperties": false, + "properties": { + "gci": { + "$ref": "#/definitions/settings/definitions/gciSettings" + }, + "gofmt": { + "$ref": "#/definitions/settings/definitions/gofmtSettings" + }, + "gofumpt": { + "$ref": "#/definitions/settings/definitions/gofumptSettings" + }, + "goimports": { + "$ref": "#/definitions/settings/definitions/goimportsSettings" + }, + "golines": { + "$ref": "#/definitions/settings/definitions/golinesSettings" + } + } + }, + "exclusions": { + "type": "object", + "additionalProperties": false, + "properties": { + "generated": { + "enum": ["strict", "lax", "disable"], + "default": "strict" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "warn-unused": { + "type": "boolean", + "default": false + } + } + } + } + }, + "issues": { + "type": "object", + "additionalProperties": false, + "properties": { + "max-issues-per-linter": { + "description": "Maximum issues count per one linter. Set to 0 to disable.", + "type": "integer", + "default": 50, + "minimum": 0 + }, + "max-same-issues": { + "description": "Maximum count of issues with the same text. Set to 0 to disable.", + "type": "integer", + "default": 3, + "minimum": 0 + }, + "new": { + "description": "Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.", + "type": "boolean", + "default": false + }, + "new-from-merge-base": { + "description": "Show only new issues created after the best common ancestor (merge-base against HEAD).", + "type": "string" + }, + "new-from-rev": { + "description": "Show only new issues created after this git revision.", + "type": "string" + }, + "new-from-patch": { + "description": "Show only new issues created in git patch with this file path.", + "type": "string", + "examples": ["path/to/patch/file"] + }, + "fix": { + "description": "Apply the fixes detected by the linters and formatters (if it's supported by the linter).", + "type": "boolean", + "default": false + }, + "uniq-by-line": { + "description": "Make issues output unique by line.", + "type": "boolean", + "default": true + }, + "whole-files": { + "description": "Show issues in any part of update files (requires new-from-rev or new-from-patch).", + "type": "boolean", + "default": false + } + } + }, + "severity": { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "description": "Set the default severity for issues. If severity rules are defined and the issues do not match or no severity is provided to the rule this will be the default severity applied. Severities should match the supported severity names of the selected out format.", + "type": "string", + "default": "" + }, + "rules": { + "description": "When a list of severity rules are provided, severity information will be added to lint issues. Severity rules have the same filtering capability as exclude rules except you are allowed to specify one matcher per severity rule.\nOnly affects out formats that support setting severity information.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "severity": { + "type": "string" + }, + "path": { + "type": "string" + }, + "path-except": { + "type": "string" + }, + "linters": { + "type": "array", + "items": { + "$ref": "#/definitions/linter-names" + } + }, + "text": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": ["severity"], + "anyOf": [ + { "required": ["path"] }, + { "required": ["path-except"] }, + { "required": ["linters"] }, + { "required": ["text"] }, + { "required": ["source"] } + ] + }, + "default": [] + } + }, + "required": ["default"] + } + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/jsonschema/jsonschema.go b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/jsonschema.go new file mode 100644 index 00000000..b380e443 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/jsonschema/jsonschema.go @@ -0,0 +1,40 @@ +package jsonschema + +import ( + "embed" + "path/filepath" + + "github.com/santhosh-tekuri/jsonschema/v6" +) + +const ( + V1Schema = "/golangci.v1.jsonschema.json" + NextSchema = "/golangci.next.jsonschema.json" +) + +//go:embed golangci.next.jsonschema.json golangci.v1.jsonschema.json +var content embed.FS + +type EmbedLoader struct { + jsonschema.FileLoader +} + +func NewEmbedLoader() *EmbedLoader { + return &EmbedLoader{} +} + +func (f *EmbedLoader) Load(uri string) (any, error) { + p, err := f.ToFile(uri) + if err != nil { + return nil, err + } + + file, err := content.Open(filepath.Base(p)) + if err != nil { + return nil, err + } + + defer func() { _ = file.Close() }() + + return jsonschema.UnmarshalJSON(file) +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/cache.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/cache.go index 9772841c..094fe630 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/cache.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/cache.go @@ -41,7 +41,7 @@ func newCacheCommand() *cacheCommand { Short: "Show cache status", Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, - Run: c.executeStatus, + RunE: c.executeStatus, }, ) @@ -51,7 +51,10 @@ func newCacheCommand() *cacheCommand { } func (*cacheCommand) executeClean(_ *cobra.Command, _ []string) error { - cacheDir := cache.DefaultDir() + cacheDir, err := cache.DefaultDir() + if err != nil { + return err + } if err := os.RemoveAll(cacheDir); err != nil { return fmt.Errorf("failed to remove dir %s: %w", cacheDir, err) @@ -60,8 +63,11 @@ func (*cacheCommand) executeClean(_ *cobra.Command, _ []string) error { return nil } -func (*cacheCommand) executeStatus(_ *cobra.Command, _ []string) { - cacheDir := cache.DefaultDir() +func (*cacheCommand) executeStatus(_ *cobra.Command, _ []string) error { + cacheDir, err := cache.DefaultDir() + if err != nil { + return err + } _, _ = fmt.Fprintf(logutils.StdOut, "Dir: %s\n", cacheDir) @@ -69,6 +75,8 @@ func (*cacheCommand) executeStatus(_ *cobra.Command, _ []string) { if err == nil { _, _ = fmt.Fprintf(logutils.StdOut, "Size: %s\n", fsutils.PrettifyBytesCount(cacheSizeBytes)) } + + return nil } func dirSizeBytes(path string) (int64, error) { diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/config.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/config.go index b1889fa4..79945eb0 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/config.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/config.go @@ -24,9 +24,8 @@ type configCommand struct { viper *viper.Viper cmd *cobra.Command - opts config.LoaderOptions - verifyOpts verifyOptions - pathOpts pathOptions + opts config.LoaderOptions + pathOpts pathOptions buildInfo BuildInfo @@ -78,11 +77,6 @@ func newConfigCommand(log logutils.Log, info BuildInfo) *configCommand { setupConfigFileFlagSet(flagSet, &c.opts) - // ex: --schema jsonschema/golangci.next.jsonschema.json - verifyFlagSet := verifyCommand.Flags() - verifyFlagSet.StringVar(&c.verifyOpts.schemaURL, "schema", "", color.GreenString("JSON schema URL")) - _ = verifyFlagSet.MarkHidden("schema") - pathFlagSet := pathCommand.Flags() pathFlagSet.BoolVar(&c.pathOpts.JSON, "json", false, color.GreenString("Display as JSON")) diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/config_verify.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/config_verify.go index 7281e088..8e99c7ee 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/config_verify.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/config_verify.go @@ -1,31 +1,23 @@ package commands import ( - "context" "encoding/json" "errors" "fmt" - "net/http" "os" "path/filepath" "strconv" "strings" - "time" - hcversion "github.com/hashicorp/go-version" "github.com/pelletier/go-toml/v2" "github.com/santhosh-tekuri/jsonschema/v6" "github.com/spf13/cobra" - "github.com/spf13/pflag" "go.yaml.in/yaml/v3" + jsonsch "github.com/golangci/golangci-lint/v2/jsonschema" "github.com/golangci/golangci-lint/v2/pkg/exitcodes" ) -type verifyOptions struct { - schemaURL string // For debugging purpose only (Flag only). -} - func (c *configCommand) executeVerify(cmd *cobra.Command, _ []string) error { usedConfigFile := c.getUsedConfig() if usedConfigFile == "" { @@ -33,14 +25,9 @@ func (c *configCommand) executeVerify(cmd *cobra.Command, _ []string) error { os.Exit(exitcodes.NoConfigFileDetected) } - schemaURL, err := createSchemaURL(cmd.Flags(), c.buildInfo) - if err != nil { - return fmt.Errorf("get JSON schema: %w", err) - } + c.log.Infof("Verifying the configuration file %q with the JSON Schema", usedConfigFile) - c.log.Infof("Verifying the configuration file %q with the JSON Schema from %s", usedConfigFile, schemaURL) - - err = validateConfiguration(schemaURL, usedConfigFile) + err := validateConfiguration(jsonsch.NextSchema, usedConfigFile) if err != nil { var v *jsonschema.ValidationError if !errors.As(err, &v) { @@ -55,85 +42,12 @@ func (c *configCommand) executeVerify(cmd *cobra.Command, _ []string) error { return nil } -func createSchemaURL(flags *pflag.FlagSet, buildInfo BuildInfo) (string, error) { - schemaURL, err := flags.GetString("schema") - if err != nil { - return "", fmt.Errorf("get schema flag: %w", err) - } - - if schemaURL != "" { - return schemaURL, nil - } - - switch { - case buildInfo.Version != "" && buildInfo.Version != "(devel)": - version, err := hcversion.NewVersion(buildInfo.Version) - if err != nil { - return "", fmt.Errorf("parse version: %w", err) - } - - if version.Core().Equal(hcversion.Must(hcversion.NewVersion("v0.0.0"))) { - commit, err := extractCommitHash(buildInfo) - if err != nil { - return "", err - } - - return fmt.Sprintf("https://raw.githubusercontent.com/golangci/golangci-lint/%s/jsonschema/golangci.next.jsonschema.json", - commit), nil - } - - return fmt.Sprintf("https://golangci-lint.run/jsonschema/golangci.v%d.%d.jsonschema.json", - version.Segments()[0], version.Segments()[1]), nil - - case buildInfo.Commit != "" && buildInfo.Commit != "?": - commit, err := extractCommitHash(buildInfo) - if err != nil { - return "", err - } - - return fmt.Sprintf("https://raw.githubusercontent.com/golangci/golangci-lint/%s/jsonschema/golangci.next.jsonschema.json", - commit), nil - - default: - return "", errors.New("version not found") - } -} - -func extractCommitHash(buildInfo BuildInfo) (string, error) { - if buildInfo.Commit == "" || buildInfo.Commit == "?" { - return "", errors.New("empty commit information") - } - - if buildInfo.Commit == "unknown" { - return "", errors.New("unknown commit information") - } - - commit := buildInfo.Commit - - if after, ok := strings.CutPrefix(commit, "("); ok { - c, _, ok := strings.Cut(after, ",") - if !ok { - return "", errors.New("commit information not found") - } - - commit = c - } - - if commit == "unknown" { - return "", errors.New("unknown commit information") - } - - return commit, nil -} - func validateConfiguration(schemaPath, targetFile string) error { compiler := jsonschema.NewCompiler() - compiler.UseLoader(jsonschema.SchemeURLLoader{ - "file": jsonschema.FileLoader{}, - "https": newJSONSchemaHTTPLoader(), - }) + compiler.UseLoader(jsonsch.NewEmbedLoader()) compiler.DefaultDraft(jsonschema.Draft7) + // The name is not us schema, err := compiler.Compile(schemaPath) if err != nil { return fmt.Errorf("compile schema: %w", err) @@ -209,33 +123,3 @@ func decodeTomlFile(filename string) (any, error) { return m, nil } - -type jsonschemaHTTPLoader struct { - *http.Client -} - -func newJSONSchemaHTTPLoader() *jsonschemaHTTPLoader { - return &jsonschemaHTTPLoader{Client: &http.Client{ - Timeout: 2 * time.Second, - }} -} - -func (l jsonschemaHTTPLoader) Load(url string) (any, error) { - req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, http.NoBody) - if err != nil { - return nil, err - } - - resp, err := l.Do(req) - if err != nil { - return nil, err - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%s returned status code %d", url, resp.StatusCode) - } - - return jsonschema.UnmarshalJSON(resp.Body) -} diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/builder.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/builder.go index 63f6f2f1..df2a134a 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/builder.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/builder.go @@ -96,6 +96,7 @@ func (b Builder) clone(ctx context.Context) error { "https://github.com/golangci/golangci-lint.git", ) cmd.Dir = b.root + cmd.Env = filterGitEnviron(os.Environ()) output, err := cmd.CombinedOutput() if err != nil { @@ -280,3 +281,33 @@ func sanitizeVersion(v string) string { return strings.Join(strings.FieldsFunc(v, fn), "") } + +// Inspired by https://github.com/pre-commit/pre-commit/blob/f5678bf4ac35cffc0ff7174ad85f7fdc2a5c977e/pre_commit/git.py#L27 +func filterGitEnviron(envs []string) []string { + var filtered []string + + for _, env := range envs { + if !strings.HasPrefix(env, "GIT_") { + filtered = append(filtered, env) + continue + } + + if strings.HasPrefix(env, "GIT_CONFIG_KEY_") || strings.HasPrefix(env, "GIT_CONFIG_VALUE_") { + filtered = append(filtered, env) + continue + } + + key, _, _ := strings.Cut(env, "=") + + switch key { + case "GIT_EXEC_PATH", "GIT_SSH", "GIT_SSH_COMMAND", "GIT_SSL_CAINFO", + "GIT_SSL_NO_VERIFY", "GIT_CONFIG_COUNT", + "GIT_HTTP_PROXY_AUTHMETHOD", + "GIT_ALLOW_PROTOCOL", + "GIT_ASKPASS": + filtered = append(filtered, env) + } + } + + return filtered +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate.go index 69b6c7c4..d44804f5 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate.go @@ -1,14 +1,13 @@ package migrate import ( - "github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/ptr" "github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/versionone" "github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/versiontwo" ) func ToConfig(old *versionone.Config) *versiontwo.Config { return &versiontwo.Config{ - Version: ptr.Pointer("2"), + Version: new("2"), Linters: toLinters(old), Formatters: toFormatters(old), Issues: toIssues(old), diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters.go index c070119b..9284da37 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters.go @@ -22,9 +22,9 @@ func toLinters(old *versionone.Config) versiontwo.Linters { func getDefaultName(old versionone.Linters) *string { switch { case ptr.Deref(old.DisableAll): - return ptr.Pointer("none") + return new("none") case ptr.Deref(old.EnableAll): - return ptr.Pointer("all") + return new("all") default: return nil // standard is the default } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters_exclusions.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters_exclusions.go index aac4d381..18c3176a 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters_exclusions.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters_exclusions.go @@ -21,7 +21,7 @@ func toExclusions(old *versionone.Config) versiontwo.LinterExclusions { func toExclusionGenerated(excludeGenerated *string) *string { if excludeGenerated == nil || ptr.Deref(excludeGenerated) == "" { - return ptr.Pointer("lax") + return new("lax") } if ptr.Deref(excludeGenerated) == "strict" { @@ -83,8 +83,8 @@ func toExclusionRules(old *versionone.Config) []versiontwo.ExcludeRule { for _, pattern := range old.Issues.ExcludePatterns { results = append(results, versiontwo.ExcludeRule{ BaseRule: versiontwo.BaseRule{ - Path: ptr.Pointer(`(.+)\.go$`), - Text: addPrefix(old.Issues, ptr.Pointer(pattern)), + Path: new(`(.+)\.go$`), + Text: addPrefix(old.Issues, new(pattern)), }, }) } @@ -102,7 +102,7 @@ func addPrefix(old versionone.Issues, s *string) *string { prefix = "(?i)" } - return ptr.Pointer(prefix + ptr.Deref(s)) + return new(prefix + ptr.Deref(s)) } func linterTestExclusions(old versionone.LintersSettings) []versiontwo.ExcludeRule { @@ -128,7 +128,7 @@ func linterTestExclusions(old versionone.LintersSettings) []versiontwo.ExcludeRu return []versiontwo.ExcludeRule{{ BaseRule: versiontwo.BaseRule{ Linters: excludedTestLinters, - Path: ptr.Pointer(`(.+)_test\.go`), + Path: new(`(.+)_test\.go`), }, }} } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters_settings.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters_settings.go index 4656842d..b831143d 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters_settings.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_linters_settings.go @@ -268,7 +268,7 @@ func toForbidigoSettings(old versionone.ForbidigoSettings) versiontwo.ForbidigoS } settings.Forbid = append(settings.Forbid, versiontwo.ForbidigoPattern{ - Pattern: ptr.Pointer(string(buffer)), + Pattern: new(string(buffer)), }) continue diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_output.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_output.go index e76f8e44..b41aa3bc 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_output.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/migrate_output.go @@ -21,52 +21,52 @@ func toOutput(old *versionone.Config) versiontwo.Output { formats.Text.PrintLinterName = old.Output.PrintLinterName formats.Text.PrintIssuedLine = old.Output.PrintIssuedLine formats.Text.Colors = nil // color is true by default (flags). - formats.Text.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.Text.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "line-number": formats.Text.PrintLinterName = old.Output.PrintLinterName formats.Text.PrintIssuedLine = old.Output.PrintIssuedLine - formats.Text.Colors = ptr.Pointer(false) - formats.Text.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.Text.Colors = new(false) + formats.Text.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "json": - formats.JSON.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.JSON.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "colored-tab": formats.Tab.PrintLinterName = old.Output.PrintLinterName formats.Tab.Colors = nil // Colors is true by default (flags). - formats.Tab.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.Tab.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "tab": formats.Tab.PrintLinterName = old.Output.PrintLinterName - formats.Tab.Colors = ptr.Pointer(false) - formats.Tab.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.Tab.Colors = new(false) + formats.Tab.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "html": - formats.HTML.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.HTML.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "checkstyle": - formats.Checkstyle.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.Checkstyle.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "code-climate": - formats.CodeClimate.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.CodeClimate.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "junit-xml": formats.JUnitXML.Extended = nil // Extended is false by default. - formats.JUnitXML.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.JUnitXML.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "junit-xml-extended": - formats.JUnitXML.Extended = ptr.Pointer(true) - formats.JUnitXML.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.JUnitXML.Extended = new(true) + formats.JUnitXML.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "github-actions": // Ignored case "teamcity": - formats.TeamCity.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.TeamCity.Path = new(defaultFormatPath(ptr.Deref(format.Path))) case "sarif": - formats.Sarif.Path = ptr.Pointer(defaultFormatPath(ptr.Deref(format.Path))) + formats.Sarif.Path = new(defaultFormatPath(ptr.Deref(format.Path))) } } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/ptr/ptr.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/ptr/ptr.go index b0c7974e..eadb6d4f 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/ptr/ptr.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/ptr/ptr.go @@ -8,5 +8,3 @@ func Deref[T any](v *T) T { return *v } - -func Pointer[T any](v T) *T { return &v } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/versionone/linters_settings.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/versionone/linters_settings.go index 3c641541..f057a4c3 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/versionone/linters_settings.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/versionone/linters_settings.go @@ -246,7 +246,7 @@ type ForbidigoPattern struct { func (p *ForbidigoPattern) UnmarshalText(text []byte) error { // Validation happens when instantiating forbidigo. - p.patternString = ptr.Pointer(string(text)) + p.patternString = new(string(text)) return nil } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/versionone/output.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/versionone/output.go index a3d86fc1..b3e29caf 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/versionone/output.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/versionone/output.go @@ -2,8 +2,6 @@ package versionone import ( "strings" - - "github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/ptr" ) type Output struct { @@ -28,8 +26,8 @@ func (p *OutputFormats) UnmarshalText(text []byte) error { format, path, _ := strings.Cut(item, ":") *p = append(*p, OutputFormat{ - Path: ptr.Pointer(path), - Format: ptr.Pointer(format), + Path: new(path), + Format: new(format), }) } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/migrate.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/migrate.go index 7012e922..cfc5eb01 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/migrate.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/migrate.go @@ -7,12 +7,13 @@ import ( "path/filepath" "strings" - "github.com/charmbracelet/lipgloss" + "charm.land/lipgloss/v2" "github.com/fatih/color" "github.com/santhosh-tekuri/jsonschema/v6" "github.com/spf13/cobra" "github.com/spf13/viper" + jsonsch "github.com/golangci/golangci-lint/v2/jsonschema" "github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate" "github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/fakeloader" "github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate/parser" @@ -153,7 +154,7 @@ func (c *migrateCommand) preRunE(cmd *cobra.Command, _ []string) error { c.log.Infof("Validating v1 configuration file: %s", usedConfigFile) - err := validateConfiguration("https://golangci-lint.run/jsonschema/golangci.v1.jsonschema.json", usedConfigFile) + err := validateConfiguration(jsonsch.V1Schema, usedConfigFile) if err != nil { var v *jsonschema.ValidationError if !errors.As(err, &v) { diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/run.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/run.go index a9a3495a..a7efa264 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/run.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/commands/run.go @@ -250,8 +250,7 @@ func (c *runCommand) execute(_ *cobra.Command, _ []string) { if err := c.runAndPrint(ctx); err != nil { c.log.Errorf("Running error: %s", err) if c.exitCode == exitcodes.Success { - var exitErr *exitcodes.ExitError - if errors.As(err, &exitErr) { + if exitErr, ok := errors.AsType[*exitcodes.ExitError](err); ok { c.exitCode = exitErr.Code } else { c.exitCode = exitcodes.Failure diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/config/base_loader.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/config/base_loader.go index 9ad332ac..1ebfc9af 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/config/base_loader.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/config/base_loader.go @@ -155,8 +155,7 @@ func (l *BaseLoader) getConfigSearchPaths() []string { func (l *BaseLoader) parseConfig() error { if err := l.viper.ReadInConfig(); err != nil { - var configFileNotFoundError viper.ConfigFileNotFoundError - if errors.As(err, &configFileNotFoundError) { + if _, ok := errors.AsType[viper.ConfigFileNotFoundError](err); ok { // Load configuration from flags only. err = l.viper.Unmarshal(l.cfg, customDecoderHook()) if err != nil { diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/config/formatters_settings.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/config/formatters_settings.go index d99354ba..bb7ad18f 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/config/formatters_settings.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/config/formatters_settings.go @@ -42,12 +42,21 @@ type GoFmtRewriteRule struct { } type GoFumptSettings struct { - ModulePath string `mapstructure:"module-path"` - ExtraRules bool `mapstructure:"extra-rules"` + ModulePath string `mapstructure:"module-path"` + Extra GoFumptExtra `mapstructure:"extra"` + + // Deprecated: use Extra instead. + ExtraRules bool `mapstructure:"extra-rules"` LangVersion string `mapstructure:"-"` } +type GoFumptExtra struct { + GroupParams bool `mapstructure:"group-params"` + ClotheReturns bool `mapstructure:"clothe-returns"` + BalanceCalls bool `mapstructure:"balance-calls"` +} + type GoImportsSettings struct { LocalPrefixes []string `mapstructure:"local-prefixes"` } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/config/linters_settings.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/config/linters_settings.go index 3f7193bd..e8561197 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/config/linters_settings.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/config/linters_settings.go @@ -41,6 +41,11 @@ var defaultLintersSettings = LintersSettings{ ExplicitExhaustiveMap: false, ExplicitExhaustiveSwitch: false, }, + Fatcontext: FatcontextSettings{ + CheckStructPointers: false, + CheckLoops: true, + CheckFunctionLiterals: true, + }, Forbidigo: ForbidigoSettings{ ExcludeGodocExamples: true, }, @@ -63,6 +68,7 @@ var defaultLintersSettings = LintersSettings{ MinOccurrencesCount: 3, NumberMin: 3, NumberMax: 3, + ExcludeTypes: []string{"Call"}, IgnoreCalls: true, }, Gocritic: GoCriticSettings{ @@ -141,17 +147,20 @@ var defaultLintersSettings = LintersSettings{ Predeclared: PredeclaredSettings{ Qualified: false, }, - SlogLint: SlogLintSettings{ - NoMixedArgs: true, - KVOnly: false, - AttrOnly: false, + Sloglint: SloglintSettings{ NoGlobal: "", Context: "", StaticMsg: false, + MsgStyle: "", + NoMixedArgs: true, + KVOnly: false, + AttrOnly: false, + ArgsOnSepLines: false, NoRawKeys: false, + AllowedKeys: []string{}, + ForbiddenKeys: []string{}, KeyNamingCase: "", - ForbiddenKeys: nil, - ArgsOnSepLines: false, + CustomFuncs: []SloglintCustomFunc{}, }, TagAlign: TagAlignSettings{ Align: true, @@ -226,13 +235,14 @@ var defaultLintersSettings = LintersSettings{ ForceExclusiveShortDeclarations: false, }, WSLv5: WSLv5Settings{ - AllowFirstInBlock: true, - AllowWholeBlock: false, - BranchMaxLines: 2, - CaseMaxLines: 0, - Default: "default", - Enable: nil, - Disable: nil, + AllowFirstInBlock: true, + AllowWholeBlock: false, + BranchMaxLines: 2, + CaseMaxLines: 0, + CuddleMaxStatements: 1, + Default: "default", + Enable: nil, + Disable: nil, }, } @@ -241,6 +251,7 @@ type LintersSettings struct { Asasalint AsasalintSettings `mapstructure:"asasalint"` BiDiChk BiDiChkSettings `mapstructure:"bidichk"` + BodyClose BodyCloseSettings `mapstructure:"bodyclose"` CopyLoopVar CopyLoopVarSettings `mapstructure:"copyloopvar"` Cyclop CyclopSettings `mapstructure:"cyclop"` Decorder DecorderSettings `mapstructure:"decorder"` @@ -254,6 +265,7 @@ type LintersSettings struct { ErrorLint ErrorLintSettings `mapstructure:"errorlint"` Exhaustive ExhaustiveSettings `mapstructure:"exhaustive"` Exhaustruct ExhaustructSettings `mapstructure:"exhaustruct"` + Exhaustructv5 ExhaustructV5Settings `mapstructure:"exhaustruct_v5"` Fatcontext FatcontextSettings `mapstructure:"fatcontext"` Forbidigo ForbidigoSettings `mapstructure:"forbidigo"` FuncOrder FuncOrderSettings `mapstructure:"funcorder"` @@ -270,6 +282,7 @@ type LintersSettings struct { Goheader GoHeaderSettings `mapstructure:"goheader"` GoModDirectives GoModDirectivesSettings `mapstructure:"gomoddirectives"` Gomodguard GoModGuardSettings `mapstructure:"gomodguard"` + Gomodguardv2 GoModGuardv2Settings `mapstructure:"gomodguard_v2"` Gosec GoSecSettings `mapstructure:"gosec"` Gosmopolitan GosmopolitanSettings `mapstructure:"gosmopolitan"` Unqueryvet UnqueryvetSettings `mapstructure:"unqueryvet"` @@ -306,7 +319,7 @@ type LintersSettings struct { Recvcheck RecvcheckSettings `mapstructure:"recvcheck"` Revive ReviveSettings `mapstructure:"revive"` RowsErrCheck RowsErrCheckSettings `mapstructure:"rowserrcheck"` - SlogLint SlogLintSettings `mapstructure:"sloglint"` + Sloglint SloglintSettings `mapstructure:"sloglint"` Spancheck SpancheckSettings `mapstructure:"spancheck"` Staticcheck StaticCheckSettings `mapstructure:"staticcheck"` TagAlign TagAlignSettings `mapstructure:"tagalign"` @@ -359,6 +372,10 @@ type BiDiChkSettings struct { PopDirectionalIsolate bool `mapstructure:"pop-directional-isolate"` } +type BodyCloseSettings struct { + CheckConsumption bool `mapstructure:"check-consumption"` +} + type CopyLoopVarSettings struct { CheckAlias bool `mapstructure:"check-alias"` } @@ -404,9 +421,10 @@ type DuplSettings struct { } type DupWordSettings struct { - Keywords []string `mapstructure:"keywords"` - Ignore []string `mapstructure:"ignore"` - CommentsOnly bool `mapstructure:"comments-only"` + Keywords []string `mapstructure:"keywords"` + Ignore []string `mapstructure:"ignore"` + CommentsOnly bool `mapstructure:"comments-only"` + SkipRawStrings bool `mapstructure:"skip-raw-strings"` } type EmbeddedStructFieldCheckSettings struct { @@ -452,6 +470,7 @@ type ExhaustiveSettings struct { DefaultCaseRequired bool `mapstructure:"default-case-required"` } +// Deprecated: use ExhaustructV5Settings instead. type ExhaustructSettings struct { Include []string `mapstructure:"include"` Exclude []string `mapstructure:"exclude"` @@ -461,8 +480,21 @@ type ExhaustructSettings struct { AllowEmptyDeclarations bool `mapstructure:"allow-empty-declarations"` } +type ExhaustructV5Settings struct { + EnforcePatterns []string `mapstructure:"enforce-patterns"` + IgnorePatterns []string `mapstructure:"ignore-patterns"` + OptionalPatterns []string `mapstructure:"optional-patterns"` + AllowEmpty bool `mapstructure:"allow-empty"` + AllowEmptyPatterns []string `mapstructure:"allow-empty-patterns"` + AllowEmptyReturns bool `mapstructure:"allow-empty-returns"` + AllowEmptyDeclarations bool `mapstructure:"allow-empty-declarations"` + ExplicitMode bool `mapstructure:"explicit-mode"` +} + type FatcontextSettings struct { - CheckStructPointers bool `mapstructure:"check-struct-pointers"` + CheckStructPointers bool `mapstructure:"check-struct-pointers"` + CheckLoops bool `mapstructure:"check-loops"` + CheckFunctionLiterals bool `mapstructure:"check-function-literals"` } type ForbidigoSettings struct { @@ -481,6 +513,7 @@ type FuncOrderSettings struct { Constructor bool `mapstructure:"constructor,omitempty"` StructMethod bool `mapstructure:"struct-method,omitempty"` Alphabetical bool `mapstructure:"alphabetical,omitempty"` + Function bool `mapstructure:"function,omitempty"` } type FunlenSettings struct { @@ -523,9 +556,20 @@ type GoConstSettings struct { ParseNumbers bool `mapstructure:"numbers"` NumberMin int `mapstructure:"min"` NumberMax int `mapstructure:"max"` - IgnoreCalls bool `mapstructure:"ignore-calls"` + ExcludeTypes []string `mapstructure:"exclude-types"` FindDuplicates bool `mapstructure:"find-duplicates"` EvalConstExpressions bool `mapstructure:"eval-const-expressions"` + IgnoreFunctions []string `mapstructure:"ignore-functions"` + IgnoreMapKeys bool `mapstructure:"ignore-map-keys"` + + // This option cannot be managed with `linters.exclusions.rules`. + // Because the linter counts occurrences across all files in the package. + IgnoreTests bool `mapstructure:"ignore-tests"` + + // NOTE(ldez): `ignore-calls` was here to have the same options as goconst as CLI. + // + // Deprecated: use ExcludeTypes instead. + IgnoreCalls bool `mapstructure:"ignore-calls"` // Deprecated: use IgnoreStringValues instead. IgnoreStrings string `mapstructure:"ignore-strings"` @@ -586,7 +630,9 @@ type GoHeaderSettings struct { type GoModDirectivesSettings struct { ReplaceAllowList []string `mapstructure:"replace-allow-list"` ReplaceLocal bool `mapstructure:"replace-local"` + ReplaceAllowAll bool `mapstructure:"replace-allow-all"` ExcludeForbidden bool `mapstructure:"exclude-forbidden"` + IgnoreForbidden bool `mapstructure:"ignore-forbidden"` RetractAllowNoExplanation bool `mapstructure:"retract-allow-no-explanation"` ToolchainForbidden bool `mapstructure:"toolchain-forbidden"` ToolchainPattern string `mapstructure:"toolchain-pattern"` @@ -596,6 +642,26 @@ type GoModDirectivesSettings struct { CheckModulePath bool `mapstructure:"check-module-path"` } +type GoModGuardv2Settings struct { + Allowed []GoModGuardv2Base `mapstructure:"allowed"` + Blocked []GoModGuardv2Blocked `mapstructure:"blocked"` + LocalReplaceDirectives bool `mapstructure:"local-replace-directives"` +} + +type GoModGuardv2Base struct { + Module string `mapstructure:"module"` + Version string `mapstructure:"version"` + MatchType string `mapstructure:"match-type"` +} + +type GoModGuardv2Blocked struct { + GoModGuardv2Base `mapstructure:",squash"` + + Recommendations []string `mapstructure:"recommendations"` + Reason string `mapstructure:"reason"` +} + +// Deprecated: use GoModGuardv2Settings instead. type GoModGuardSettings struct { Allowed GoModGuardAllowed `mapstructure:"allowed"` Blocked GoModGuardBlocked `mapstructure:"blocked"` @@ -792,13 +858,15 @@ type NoLintLintSettings struct { } type NoNamedReturnsSettings struct { - ReportErrorInDefer bool `mapstructure:"report-error-in-defer"` + ReportErrorInDefer bool `mapstructure:"report-error-in-defer"` + AllowUnusedNamedReturns bool `mapstructure:"allow-unused-named-returns"` } type ParallelTestSettings struct { Go string `mapstructure:"-"` IgnoreMissing bool `mapstructure:"ignore-missing"` IgnoreMissingSubtests bool `mapstructure:"ignore-missing-subtests"` + CheckCleanup bool `mapstructure:"check-cleanup"` } type PerfSprintSettings struct { @@ -882,18 +950,26 @@ type RowsErrCheckSettings struct { Packages []string `mapstructure:"packages"` } -type SlogLintSettings struct { - NoMixedArgs bool `mapstructure:"no-mixed-args"` - KVOnly bool `mapstructure:"kv-only"` - AttrOnly bool `mapstructure:"attr-only"` - NoGlobal string `mapstructure:"no-global"` - Context string `mapstructure:"context"` - StaticMsg bool `mapstructure:"static-msg"` - MsgStyle string `mapstructure:"msg-style"` - NoRawKeys bool `mapstructure:"no-raw-keys"` - KeyNamingCase string `mapstructure:"key-naming-case"` - ForbiddenKeys []string `mapstructure:"forbidden-keys"` - ArgsOnSepLines bool `mapstructure:"args-on-sep-lines"` +type SloglintSettings struct { + NoGlobal string `mapstructure:"no-global"` + Context string `mapstructure:"context"` + StaticMsg bool `mapstructure:"static-msg"` + MsgStyle string `mapstructure:"msg-style"` + NoMixedArgs bool `mapstructure:"no-mixed-args"` + KVOnly bool `mapstructure:"kv-only"` + AttrOnly bool `mapstructure:"attr-only"` + ArgsOnSepLines bool `mapstructure:"args-on-sep-lines"` + NoRawKeys bool `mapstructure:"no-raw-keys"` + AllowedKeys []string `mapstructure:"allowed-keys"` + ForbiddenKeys []string `mapstructure:"forbidden-keys"` + KeyNamingCase string `mapstructure:"key-naming-case"` + CustomFuncs []SloglintCustomFunc `mapstructure:"custom-funcs"` +} + +type SloglintCustomFunc struct { + Name string `mapstructure:"name"` + MsgPos int `mapstructure:"msg-pos"` + ArgsPos int `mapstructure:"args-pos"` } type SpancheckSettings struct { @@ -1131,13 +1207,14 @@ type WSLv4Settings struct { } type WSLv5Settings struct { - AllowFirstInBlock bool `mapstructure:"allow-first-in-block"` - AllowWholeBlock bool `mapstructure:"allow-whole-block"` - BranchMaxLines int `mapstructure:"branch-max-lines"` - CaseMaxLines int `mapstructure:"case-max-lines"` - Default string `mapstructure:"default"` - Enable []string `mapstructure:"enable"` - Disable []string `mapstructure:"disable"` + AllowFirstInBlock bool `mapstructure:"allow-first-in-block"` + AllowWholeBlock bool `mapstructure:"allow-whole-block"` + BranchMaxLines int `mapstructure:"branch-max-lines"` + CaseMaxLines int `mapstructure:"case-max-lines"` + CuddleMaxStatements int `mapstructure:"cuddle-max-statements"` + Default string `mapstructure:"default"` + Enable []string `mapstructure:"enable"` + Disable []string `mapstructure:"disable"` } // CustomLinterSettings encapsulates the meta-data of a private linter. diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_action_cache.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_action_cache.go index 1fafbca5..8fcacf6e 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_action_cache.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_action_cache.go @@ -30,35 +30,74 @@ func (act *action) loadCachedFacts() bool { return true // no need to load facts } - return act.loadPersistedFacts() + if !act.loadPersistedFacts() { + return false + } + + // The cache only stores the facts a package produces about its own objects. + // The facts a package re-exports from its dependencies (see exportedFrom) are not persisted, + // to avoid duplicating them in every cache entry (which grows quadratically with the import graph). + // Instead, we rebuild them in memory here by inheriting from the dependencies, + // exactly like analyze() does for packages analyzed from source. + // This is safe because every dependency is fully analyzed (from cache or source) before this package is loaded, + // so their facts are already available. + act.inheritFactsFromDeps() + + return true }() + act.loadCachedFactsDone = true act.loadCachedFactsOk = res + return res } +// inheritFactsFromDeps rebuilds, in memory, +// the facts re-exported from this action's dependencies (vertical edges: same analyzer, different package), +// mirroring the inheritance performed by analyze() for packages analyzed from source. +func (act *action) inheritFactsFromDeps() { + for _, dep := range act.Deps { + if dep.Package == act.Package || dep.Analyzer != act.Analyzer { + continue + } + + inheritFacts(act, dep) + } +} + func (act *action) persistFactsToCache() error { analyzer := act.Analyzer + if len(analyzer.FactTypes) == 0 { return nil } - // Merge new facts into the package and persist them. + // Persist only the facts this package produces about its own objects. + // + // Facts about objects from other packages (inherited through this package's export data) are intentionally NOT persisted: + // doing so duplicates them in every cache entry along the import graph and makes the cache grow quadratically. + // When a package is restored from the cache, + // those re-exported facts are rebuilt in memory by inheriting from its dependencies (see inheritFactsFromDeps). + var facts []Fact + for key, fact := range act.packageFacts { if key.pkg != act.Package.Types { - // The fact is from inherited facts from another package + // The fact is inherited from another package. continue } + facts = append(facts, Fact{ Path: "", Fact: fact, }) } + for key, fact := range act.objectFacts { obj := key.obj + if obj.Pkg() != act.Package.Types { - // The fact is from inherited facts from another package + // The fact is inherited from another package. continue } @@ -89,6 +128,7 @@ func (act *action) loadPersistedFacts() bool { } factsCacheDebugf("No cached facts for package %q and analyzer %s", act.Package.Name, act.Analyzer.Name) + return false } @@ -100,6 +140,7 @@ func (act *action) loadPersistedFacts() bool { act.packageFacts[key] = f.Fact continue } + obj, err := objectpath.Object(act.Package.Types, objectpath.Path(f.Path)) if err != nil { // Be lenient about these errors. diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_loadingpackage.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_loadingpackage.go index e01d3eaa..f918be63 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_loadingpackage.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_loadingpackage.go @@ -531,8 +531,8 @@ func sizeOfReflectValueTreeBytes(rv reflect.Value, visitedPtrs map[uintptr]struc return sizeOfReflectValueTreeBytes(rv.Elem(), visitedPtrs) case reflect.Struct: ret := 0 - for i := range rv.NumField() { - ret += sizeOfReflectValueTreeBytes(rv.Field(i), visitedPtrs) + for _, field := range rv.Fields() { + ret += sizeOfReflectValueTreeBytes(field, visitedPtrs) } return ret case reflect.Slice, reflect.Array, reflect.Chan: diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gci/internal/section/standard_list.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gci/internal/section/standard_list.go index a7787409..120acb13 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gci/internal/section/standard_list.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gci/internal/section/standard_list.go @@ -1,6 +1,6 @@ package section -// Code generated based on go1.26.0 X:boringcrypto,arenas,jsonv2,runtimesecret. DO NOT EDIT. +// Code generated based on go1.27.0 Thu May 28 17:10:42 2026 -0700 X:boringcrypto,arenas,runtimesecret. DO NOT EDIT. var standardPackages = map[string]struct{}{ "archive/tar": {}, @@ -33,6 +33,7 @@ var standardPackages = map[string]struct{}{ "crypto/hmac": {}, "crypto/hpke": {}, "crypto/md5": {}, + "crypto/mldsa": {}, "crypto/mlkem": {}, "crypto/mlkem/mlkemtest": {}, "crypto/pbkdf2": {}, @@ -185,5 +186,6 @@ var standardPackages = map[string]struct{}{ "unicode/utf8": {}, "unique": {}, "unsafe": {}, + "uuid": {}, "weak": {}, } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gofmt/gofmt.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gofmt/gofmt.go index 4d5ff863..38660a59 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gofmt/gofmt.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gofmt/gofmt.go @@ -1,7 +1,7 @@ package gofmt import ( - "github.com/golangci/gofmt/gofmt" + "github.com/golangci/gofmt" "github.com/golangci/golangci-lint/v2/pkg/config" ) diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gofumpt/gofumpt.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gofumpt/gofumpt.go index d8f5f2f7..1e3f7100 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gofumpt/gofumpt.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/goformatters/gofumpt/gofumpt.go @@ -6,6 +6,7 @@ import ( gofumpt "mvdan.cc/gofumpt/format" "github.com/golangci/golangci-lint/v2/pkg/config" + "github.com/golangci/golangci-lint/v2/pkg/goformatters/internal" ) const Name = "gofumpt" @@ -18,10 +19,19 @@ func New(settings *config.GoFumptSettings, goVersion string) *Formatter { var options gofumpt.Options if settings != nil { + if settings.ExtraRules { + internal.FormatterLogger.Warnf("gofumpt: `extra-rules` is deprecated, please use `extra.group-params` instead.") + } + options = gofumpt.Options{ LangVersion: getLangVersion(goVersion), ModulePath: settings.ModulePath, ExtraRules: settings.ExtraRules, + Extra: gofumpt.Extra{ + GroupParams: settings.Extra.GroupParams, + ClotheReturns: settings.Extra.ClotheReturns, + BalanceCalls: settings.Extra.BalanceCalls, + }, } } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/bodyclose/bodyclose.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/bodyclose/bodyclose.go index f68c4d0a..95cf790b 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/bodyclose/bodyclose.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/bodyclose/bodyclose.go @@ -3,11 +3,21 @@ package bodyclose import ( "github.com/timakin/bodyclose/passes/bodyclose" + "github.com/golangci/golangci-lint/v2/pkg/config" "github.com/golangci/golangci-lint/v2/pkg/goanalysis" ) -func New() *goanalysis.Linter { +func New(settings *config.BodyCloseSettings) *goanalysis.Linter { + var cfg map[string]any + + if settings != nil { + cfg = map[string]any{ + "check-consumption": settings.CheckConsumption, + } + } + return goanalysis. NewLinterFromAnalyzer(bodyclose.Analyzer). + WithConfig(cfg). WithLoadMode(goanalysis.LoadModeTypesInfo) } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/clickhouselint/clickhouselint.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/clickhouselint/clickhouselint.go new file mode 100644 index 00000000..0459864b --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/clickhouselint/clickhouselint.go @@ -0,0 +1,18 @@ +package clickhouselint + +import ( + "github.com/ClickHouse/clickhouse-go-linter/passes/chbatchclose" + "github.com/ClickHouse/clickhouse-go-linter/passes/chrowserr" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/v2/pkg/goanalysis" +) + +func New() *goanalysis.Linter { + return goanalysis.NewLinter( + "clickhouselint", + "Detects common mistakes with the ClickHouse native Go driver API.", + []*analysis.Analyzer{chrowserr.NewAnalyzer(), chbatchclose.NewAnalyzer()}, + nil, + ).WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/dupword/dupword.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/dupword/dupword.go index 0308534e..08ae1868 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/dupword/dupword.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/dupword/dupword.go @@ -14,9 +14,10 @@ func New(settings *config.DupWordSettings) *goanalysis.Linter { if settings != nil { cfg = map[string]any{ - "keyword": strings.Join(settings.Keywords, ","), - "ignore": strings.Join(settings.Ignore, ","), - "comments-only": settings.CommentsOnly, + "keyword": strings.Join(settings.Keywords, ","), + "ignore": strings.Join(settings.Ignore, ","), + "comments-only": settings.CommentsOnly, + "skip-raw-strings": settings.SkipRawStrings, } } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/exhaustruct/exhaustruct_v5.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/exhaustruct/exhaustruct_v5.go new file mode 100644 index 00000000..eabfe1ae --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/exhaustruct/exhaustruct_v5.go @@ -0,0 +1,34 @@ +package exhaustruct + +import ( + exhaustruct "dev.gaijin.team/go/exhaustruct/v5/analyzer" + + "github.com/golangci/golangci-lint/v2/pkg/config" + "github.com/golangci/golangci-lint/v2/pkg/goanalysis" + "github.com/golangci/golangci-lint/v2/pkg/golinters/internal" +) + +func NewV5(settings *config.ExhaustructV5Settings) *goanalysis.Linter { + cfg := exhaustruct.Config{} + + if settings != nil { + cfg.EnforcePatterns = settings.EnforcePatterns + cfg.IgnorePatterns = settings.IgnorePatterns + cfg.OptionalPatterns = settings.OptionalPatterns + cfg.AllowEmpty = settings.AllowEmpty + cfg.AllowEmptyPatterns = settings.AllowEmptyPatterns + cfg.AllowEmptyReturns = settings.AllowEmptyReturns + cfg.AllowEmptyDeclarations = settings.AllowEmptyDeclarations + cfg.ExplicitMode = settings.ExplicitMode + } + + analyzer, err := exhaustruct.NewAnalyzerWithConfig(cfg) + if err != nil { + internal.LinterLogger.Fatalf("exhaustruct configuration: %v", err) + } + + return goanalysis. + NewLinterFromAnalyzer(analyzer). + WithVersion(5). //nolint:mnd // It's the linter version. + WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/fatcontext/fatcontext.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/fatcontext/fatcontext.go index 58933c66..656fd423 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/fatcontext/fatcontext.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/fatcontext/fatcontext.go @@ -12,7 +12,9 @@ func New(settings *config.FatcontextSettings) *goanalysis.Linter { if settings != nil { cfg = map[string]any{ - analyzer.FlagCheckStructPointers: settings.CheckStructPointers, + analyzer.FlagCheckStructPointers: settings.CheckStructPointers, + analyzer.FlagCheckLoops: settings.CheckLoops, + analyzer.FlagCheckFunctionLiterals: settings.CheckFunctionLiterals, } } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/funcorder/funcorder.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/funcorder/funcorder.go index 06400753..aa918896 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/funcorder/funcorder.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/funcorder/funcorder.go @@ -15,6 +15,7 @@ func New(settings *config.FuncOrderSettings) *goanalysis.Linter { analyzer.ConstructorCheckName: settings.Constructor, analyzer.StructMethodCheckName: settings.StructMethod, analyzer.AlphabeticalCheckName: settings.Alphabetical, + analyzer.FunctionCheckName: settings.Function, } } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/goconst/goconst.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/goconst/goconst.go index b58d860c..4320c44c 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/goconst/goconst.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/goconst/goconst.go @@ -2,6 +2,7 @@ package goconst import ( "fmt" + "strings" "sync" goconstAPI "github.com/jgautheron/goconst" @@ -48,8 +49,9 @@ func New(settings *config.GoConstSettings) *goanalysis.Linter { } func runGoconst(pass *analysis.Pass, settings *config.GoConstSettings) ([]*goanalysis.Issue, error) { - cfg := goconstAPI.Config{ + cfg := &goconstAPI.Config{ IgnoreStrings: settings.IgnoreStringValues, + IgnoreTests: settings.IgnoreTests, MatchWithConstants: settings.MatchWithConstants, MinStringLength: settings.MinStringLen, MinOccurrences: settings.MinOccurrencesCount, @@ -59,16 +61,28 @@ func runGoconst(pass *analysis.Pass, settings *config.GoConstSettings) ([]*goana ExcludeTypes: map[goconstAPI.Type]bool{}, FindDuplicates: settings.FindDuplicates, EvalConstExpressions: settings.EvalConstExpressions, + IgnoreFunctions: settings.IgnoreFunctions, + IgnoreMapKeys: settings.IgnoreMapKeys, + } - // Should be managed with `linters.exclusions.rules`. - IgnoreTests: false, + // There is no deprecation log for `IgnoreCalls` because the default is true. + // As the default of `ExcludeTypes` contains `call`, + // setting `IgnoreCalls` to false is the only way for a user to explicitly use the value `call`. + // TODO(ldez): `IgnoreCalls` must be removed if the next major version. + if !settings.IgnoreCalls && len(settings.ExcludeTypes) == 1 && strings.EqualFold(settings.ExcludeTypes[0], "call") { + settings.ExcludeTypes = nil } - if settings.IgnoreCalls { - cfg.ExcludeTypes[goconstAPI.Call] = true + for _, k := range settings.ExcludeTypes { + typ, err := toType(k) + if err != nil { + return nil, err + } + + cfg.ExcludeTypes[typ] = true } - lintIssues, err := goconstAPI.Run(pass.Files, pass.Fset, pass.TypesInfo, &cfg) + lintIssues, err := goconstAPI.Run(pass.Files, pass.Fset, pass.TypesInfo, cfg) if err != nil { return nil, err } @@ -111,3 +125,28 @@ func runGoconst(pass *analysis.Pass, settings *config.GoConstSettings) ([]*goana return res, nil } + +func toType(v string) (goconstAPI.Type, error) { + switch strings.ToLower(v) { + case "assignment": + return goconstAPI.Assignment, nil + + case "binary": + return goconstAPI.Binary, nil + + case "case": + return goconstAPI.Case, nil + + case "return": + return goconstAPI.Return, nil + + case "call": + return goconstAPI.Call, nil + + case "compositelit": + return goconstAPI.CompositeLit, nil + + default: + return 0, fmt.Errorf("unknown type %s", v) + } +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/godoclint/godoclint.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/godoclint/godoclint.go index 0d017125..26c98adb 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/godoclint/godoclint.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/godoclint/godoclint.go @@ -46,18 +46,18 @@ func New(settings *config.GodoclintSettings) *goanalysis.Linter { Disable: settings.Disable, Options: &glconfig.PlainRuleOptions{ MaxLenLength: settings.Options.MaxLen.Length, - MaxLenIncludeTests: pointer(true), + MaxLenIncludeTests: new(true), MaxLenIgnorePatterns: []string{`^\+kubebuilder:`}, - PkgDocIncludeTests: pointer(false), - SinglePkgDocIncludeTests: pointer(true), - RequirePkgDocIncludeTests: pointer(false), - RequireDocIncludeTests: pointer(true), + PkgDocIncludeTests: new(false), + SinglePkgDocIncludeTests: new(true), + RequirePkgDocIncludeTests: new(false), + RequireDocIncludeTests: new(true), RequireDocIgnoreExported: settings.Options.RequireDoc.IgnoreExported, RequireDocIgnoreUnexported: settings.Options.RequireDoc.IgnoreUnexported, - StartWithNameIncludeTests: pointer(false), + StartWithNameIncludeTests: new(false), StartWithNameIncludeUnexported: settings.Options.StartWithName.IncludeUnexported, - RequireStdlibDoclinkIncludeTests: pointer(true), - NoUnusedLinkIncludeTests: pointer(true), + RequireStdlibDoclinkIncludeTests: new(true), + NoUnusedLinkIncludeTests: new(true), }, } @@ -107,8 +107,6 @@ func checkSettings(settings *config.GodoclintSettings) error { return nil } -func pointer[T any](v T) *T { return &v } - func deref[T any](v *T) T { if v == nil { var zero T diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gofumpt/gofumpt.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gofumpt/gofumpt.go index 1ee7c833..69f61799 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gofumpt/gofumpt.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gofumpt/gofumpt.go @@ -13,7 +13,7 @@ func New(settings *config.GoFumptSettings) *goanalysis.Linter { NewLinterFromAnalyzer( goformatters.NewAnalyzer( internal.LinterLogger.Child(gofumptbase.Name), - "Check if code and import statements are formatted, with additional rules.", + "Enforce a stricter format than gofmt, while being backwards compatible.", gofumptbase.New(settings, settings.LangVersion), ), ). diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomoddirectives/gomoddirectives.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomoddirectives/gomoddirectives.go index 817b5498..71c42c4e 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomoddirectives/gomoddirectives.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomoddirectives/gomoddirectives.go @@ -24,8 +24,10 @@ func New(settings *config.GoModDirectivesSettings) *goanalysis.Linter { if settings != nil { opts.ReplaceAllowLocal = settings.ReplaceLocal opts.ReplaceAllowList = settings.ReplaceAllowList + opts.ReplaceAllowAll = settings.ReplaceAllowAll opts.RetractAllowNoExplanation = settings.RetractAllowNoExplanation opts.ExcludeForbidden = settings.ExcludeForbidden + opts.IgnoreForbidden = settings.IgnoreForbidden opts.ToolchainForbidden = settings.ToolchainForbidden opts.ToolForbidden = settings.ToolForbidden opts.GoDebugForbidden = settings.GoDebugForbidden diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomodguard/gomodguard.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomodguard/gomodguard.go index 7d16f57b..b8bdd096 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomodguard/gomodguard.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomodguard/gomodguard.go @@ -87,3 +87,66 @@ func New(settings *config.GoModGuardSettings) *goanalysis.Linter { }). WithLoadMode(goanalysis.LoadModeSyntax) } + +// Only used the set YAML struct tags. +type v2YAML struct { + Allowed []goModGuardv2Base `yaml:"allowed,omitempty"` + Blocked []goModGuardv2Blocked `yaml:"blocked,omitempty"` + LocalReplaceDirectives bool `yaml:"local-replace-directives,omitempty"` +} + +// Only used the set YAML struct tags. +type goModGuardv2Base struct { + Module string `yaml:"module,omitempty"` + Version string `yaml:"version,omitempty"` + MatchType string `yaml:"match-type,omitempty"` +} + +// Only used the set YAML struct tags. +type goModGuardv2Blocked struct { + goModGuardv2Base `yaml:",inline"` + + Recommendations []string `yaml:"recommendations,omitempty"` + Reason string `yaml:"reason,omitempty"` +} + +func Migration(old *config.GoModGuardSettings) any { + if old == nil { + return nil + } + + if len(old.Allowed.Modules) == 0 && len(old.Allowed.Domains) == 0 && len(old.Blocked.Modules) == 0 && !old.Blocked.LocalReplaceDirectives { + return nil + } + + cfg := &v2YAML{ + LocalReplaceDirectives: old.Blocked.LocalReplaceDirectives, + } + + for _, module := range old.Allowed.Modules { + cfg.Allowed = append(cfg.Allowed, goModGuardv2Base{ + Module: module, + }) + } + + for _, domain := range old.Allowed.Domains { + cfg.Allowed = append(cfg.Allowed, goModGuardv2Base{ + Module: domain + "/.*", + MatchType: "regex", + }) + } + + for _, blocked := range old.Blocked.Modules { + for name, module := range blocked { + cfg.Blocked = append(cfg.Blocked, goModGuardv2Blocked{ + goModGuardv2Base: goModGuardv2Base{ + Module: name, + }, + Recommendations: module.Recommendations, + Reason: module.Reason, + }) + } + } + + return cfg +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomodguard/gomodguard_v2.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomodguard/gomodguard_v2.go new file mode 100644 index 00000000..d25e7206 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/gomodguard/gomodguard_v2.go @@ -0,0 +1,106 @@ +package gomodguard + +import ( + "sync" + + "github.com/Masterminds/semver/v3" + "github.com/ryancurrah/gomodguard/v2" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/v2/pkg/config" + "github.com/golangci/golangci-lint/v2/pkg/goanalysis" + "github.com/golangci/golangci-lint/v2/pkg/golinters/internal" + "github.com/golangci/golangci-lint/v2/pkg/lint/linter" + "github.com/golangci/golangci-lint/v2/pkg/result" +) + +const linterNameV2 = "gomodguard_v2" + +func NewV2(settings *config.GoModGuardv2Settings) *goanalysis.Linter { + var issues []*goanalysis.Issue + var mu sync.Mutex + + processorCfg := &gomodguard.Configuration{} + if settings != nil { + processorCfg.LocalReplaceDirectives = settings.LocalReplaceDirectives + + for _, allowed := range settings.Allowed { + rule := gomodguard.AllowedModule{ + Module: allowed.Module, + MatchType: gomodguard.MatchType(allowed.MatchType), + Version: nil, + Matcher: nil, + } + + if allowed.Version != "" { + var err error + + rule.Version, err = semver.NewConstraint(allowed.Version) + if err != nil { + internal.LinterLogger.Fatalf("gomodguard: invalid constraint: %v", err) + } + } + + processorCfg.Allowed = append(processorCfg.Allowed, rule) + } + + for _, blocked := range settings.Blocked { + rule := gomodguard.BlockedModule{ + Module: blocked.Module, + MatchType: gomodguard.MatchType(blocked.MatchType), + Recommendations: blocked.Recommendations, + Reason: blocked.Reason, + } + + if blocked.Version != "" { + var err error + + rule.Version, err = semver.NewConstraint(blocked.Version) + if err != nil { + internal.LinterLogger.Fatalf("gomodguard: invalid constraint: %v", err) + } + } + + processorCfg.Blocked = append(processorCfg.Blocked, rule) + } + } + + analyzer := &analysis.Analyzer{ + Name: linterNameV2, + Doc: "Allow and blocklist linter for direct Go module dependencies. " + + "This is different from depguard where there are different block " + + "types for example version constraints and module recommendations.", + Run: goanalysis.DummyRun, + } + + return goanalysis.NewLinterFromAnalyzer(analyzer). + WithContextSetter(func(lintCtx *linter.Context) { + processor, err := gomodguard.NewProcessor(processorCfg) + if err != nil { + lintCtx.Log.Warnf("running gomodguard failed: %s: if you are not using go modules "+ + "it is suggested to disable this linter", err) + return + } + + analyzer.Run = func(pass *analysis.Pass) (any, error) { + gomodguardIssues := processor.ProcessFiles(internal.GetGoFileNames(pass)) + + mu.Lock() + defer mu.Unlock() + + for _, gomodguardIssue := range gomodguardIssues { + issues = append(issues, goanalysis.NewIssue(&result.Issue{ + FromLinter: linterNameV2, + Pos: gomodguardIssue.Position, + Text: gomodguardIssue.Reason, + }, pass)) + } + + return nil, nil + } + }). + WithIssuesReporter(func(*linter.Context) []*goanalysis.Issue { + return issues + }). + WithLoadMode(goanalysis.LoadModeSyntax) +} diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/govet/govet.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/govet/govet.go index 7755e4ec..9f98f405 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/govet/govet.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/govet/govet.go @@ -28,6 +28,7 @@ import ( "golang.org/x/tools/go/analysis/passes/httpmux" "golang.org/x/tools/go/analysis/passes/httpresponse" "golang.org/x/tools/go/analysis/passes/ifaceassert" + "golang.org/x/tools/go/analysis/passes/inline" _ "golang.org/x/tools/go/analysis/passes/inspect" // unused internal analyzer "golang.org/x/tools/go/analysis/passes/loopclosure" "golang.org/x/tools/go/analysis/passes/lostcancel" @@ -83,6 +84,7 @@ var ( httpmux.Analyzer, httpresponse.Analyzer, ifaceassert.Analyzer, + inline.Analyzer, loopclosure.Analyzer, lostcancel.Analyzer, nilfunc.Analyzer, @@ -109,7 +111,8 @@ var ( waitgroup.Analyzer, } - // https://github.com/golang/go/blob/go1.25.2/src/cmd/vet/main.go#L57-L91 + // https://github.com/golang/go/blob/go1.26.1/src/cmd/vet/main.go#L63-L99 + // https://github.com/golang/go/blob/go1.26.1/src/cmd/fix/main.go#L47-L51 defaultAnalyzers = []*analysis.Analyzer{ appends.Analyzer, asmdecl.Analyzer, @@ -127,6 +130,7 @@ var ( hostport.Analyzer, httpresponse.Analyzer, ifaceassert.Analyzer, + inline.Analyzer, loopclosure.Analyzer, lostcancel.Analyzer, nilfunc.Analyzer, diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/iface/iface.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/iface/iface.go index 0a4a38ab..03330fda 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/iface/iface.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/iface/iface.go @@ -7,6 +7,7 @@ import ( "github.com/uudashr/iface/opaque" "github.com/uudashr/iface/unexported" "github.com/uudashr/iface/unused" + "github.com/uudashr/iface/unusedmethod" "golang.org/x/tools/go/analysis" "github.com/golangci/golangci-lint/v2/pkg/config" @@ -29,10 +30,11 @@ func New(settings *config.IfaceSettings) *goanalysis.Linter { func analyzersFromSettings(settings *config.IfaceSettings) []*analysis.Analyzer { allAnalyzers := map[string]*analysis.Analyzer{ - "identical": identical.Analyzer, - "unused": unused.Analyzer, - "opaque": opaque.Analyzer, - "unexported": unexported.Analyzer, + "identical": identical.Analyzer, + "unused": unused.Analyzer, + "opaque": opaque.Analyzer, + "unexported": unexported.Analyzer, + "unusedmethod": unusedmethod.Analyzer, } if settings == nil || len(settings.Enable) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/modernize/modernize.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/modernize/modernize.go index 97825c07..6878c01e 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/modernize/modernize.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/modernize/modernize.go @@ -5,6 +5,7 @@ import ( "github.com/golangci/golangci-lint/v2/pkg/config" "github.com/golangci/golangci-lint/v2/pkg/goanalysis" + "github.com/golangci/golangci-lint/v2/pkg/golinters/internal" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/modernize" @@ -16,6 +17,15 @@ func New(settings *config.ModernizeSettings) *goanalysis.Linter { if settings == nil { analyzers = modernize.Suite } else { + for _, name := range settings.Disable { + switch name { + case "fmtappendf": + internal.LinterLogger.Warnf("%s has been removed from the modernize suite", name) + case "waitgroup": + internal.LinterLogger.Warnf("%s has been renamed to 'waitgroupgo'", name) + } + } + for _, analyzer := range modernize.Suite { if slices.Contains(settings.Disable, analyzer.Name) { continue diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/nonamedreturns/nonamedreturns.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/nonamedreturns/nonamedreturns.go index 4149ef81..0dd71e28 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/nonamedreturns/nonamedreturns.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/nonamedreturns/nonamedreturns.go @@ -12,7 +12,8 @@ func New(settings *config.NoNamedReturnsSettings) *goanalysis.Linter { if settings != nil { cfg = map[string]any{ - analyzer.FlagReportErrorInDefer: settings.ReportErrorInDefer, + analyzer.FlagReportErrorInDefer: settings.ReportErrorInDefer, + analyzer.FlagAllowUnusedNamedReturns: settings.AllowUnusedNamedReturns, } } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/paralleltest/paralleltest.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/paralleltest/paralleltest.go index f3eac2e0..be052628 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/paralleltest/paralleltest.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/paralleltest/paralleltest.go @@ -14,6 +14,7 @@ func New(settings *config.ParallelTestSettings) *goanalysis.Linter { cfg = map[string]any{ "i": settings.IgnoreMissing, "ignoremissingsubtests": settings.IgnoreMissingSubtests, + "checkcleanup": settings.CheckCleanup, } if config.IsGoGreaterThanOrEqual(settings.Go, "1.22") { diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/revive/revive.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/revive/revive.go index 9dc84537..7fe736f2 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/revive/revive.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/revive/revive.go @@ -209,9 +209,11 @@ func getConfig(cfg *config.ReviveSettings) (*lint.Config, error) { } func createConfigMap(cfg *config.ReviveSettings) map[string]any { + const severity = "severity" + rawRoot := map[string]any{ "confidence": cfg.Confidence, - "severity": cfg.Severity, + severity: cfg.Severity, "errorCode": cfg.ErrorCode, "warningCode": cfg.WarningCode, "enableAllRules": cfg.EnableAllRules, @@ -224,7 +226,7 @@ func createConfigMap(cfg *config.ReviveSettings) map[string]any { rawDirectives := map[string]map[string]any{} for _, directive := range cfg.Directives { rawDirectives[directive.Name] = map[string]any{ - "severity": directive.Severity, + severity: directive.Severity, } } @@ -235,7 +237,7 @@ func createConfigMap(cfg *config.ReviveSettings) map[string]any { rawRules := map[string]map[string]any{} for _, s := range cfg.Rules { rawRules[s.Name] = map[string]any{ - "severity": s.Severity, + severity: s.Severity, "arguments": safeTomlSlice(s.Arguments), "disabled": s.Disabled, "exclude": s.Exclude, diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/rowserrcheck/rowserrcheck.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/rowserrcheck/rowserrcheck.go index de0fe4da..57299039 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/rowserrcheck/rowserrcheck.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/rowserrcheck/rowserrcheck.go @@ -1,7 +1,7 @@ package rowserrcheck import ( - "github.com/jingyugao/rowserrcheck/passes/rowserr" + "github.com/golangci/rowserrcheck/passes/rowserr" "github.com/golangci/golangci-lint/v2/pkg/config" "github.com/golangci/golangci-lint/v2/pkg/goanalysis" diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/sloglint/sloglint.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/sloglint/sloglint.go index 891f1fcf..2db696e5 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/sloglint/sloglint.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/sloglint/sloglint.go @@ -7,22 +7,31 @@ import ( "github.com/golangci/golangci-lint/v2/pkg/goanalysis" ) -func New(settings *config.SlogLintSettings) *goanalysis.Linter { +func New(settings *config.SloglintSettings) *goanalysis.Linter { var opts *sloglint.Options if settings != nil { opts = &sloglint.Options{ - NoMixedArgs: settings.NoMixedArgs, - KVOnly: settings.KVOnly, - AttrOnly: settings.AttrOnly, - NoGlobal: settings.NoGlobal, - ContextOnly: settings.Context, - StaticMsg: settings.StaticMsg, - MsgStyle: settings.MsgStyle, - NoRawKeys: settings.NoRawKeys, - KeyNamingCase: settings.KeyNamingCase, - ForbiddenKeys: settings.ForbiddenKeys, - ArgsOnSepLines: settings.ArgsOnSepLines, + NoGlobalLogger: settings.NoGlobal, + ContextOnly: settings.Context, + StaticMessage: settings.StaticMsg, + MessageStyle: settings.MsgStyle, + NoMixedArguments: settings.NoMixedArgs, + KeyValuePairsOnly: settings.KVOnly, + AttributesOnly: settings.AttrOnly, + ArgumentsOnSeparateLines: settings.ArgsOnSepLines, + ConstantKeys: settings.NoRawKeys, + AllowedKeys: settings.AllowedKeys, + ForbiddenKeys: settings.ForbiddenKeys, + KeyNamingCase: settings.KeyNamingCase, + } + + for _, fn := range settings.CustomFuncs { + opts.CustomFuncs = append(opts.CustomFuncs, sloglint.Func{ + FullName: fn.Name, + MessagePos: fn.MsgPos, + ArgumentsPos: fn.ArgsPos, + }) } } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/wsl/wsl_v5.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/wsl/wsl_v5.go index cb7b2562..8b8eef3f 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/wsl/wsl_v5.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/golinters/wsl/wsl_v5.go @@ -18,12 +18,13 @@ func NewV5(settings *config.WSLv5Settings) *goanalysis.Linter { } conf = &wsl.Configuration{ - IncludeGenerated: true, // force to true because golangci-lint already has a way to filter generated files. - AllowFirstInBlock: settings.AllowFirstInBlock, - AllowWholeBlock: settings.AllowWholeBlock, - BranchMaxLines: settings.BranchMaxLines, - CaseMaxLines: settings.CaseMaxLines, - Checks: checkSet, + IncludeGenerated: true, // force to true because golangci-lint already has a way to filter generated files. + AllowFirstInBlock: settings.AllowFirstInBlock, + AllowWholeBlock: settings.AllowWholeBlock, + BranchMaxLines: settings.BranchMaxLines, + CaseMaxLines: settings.CaseMaxLines, + CuddleMaxStatements: settings.CuddleMaxStatements, + Checks: checkSet, } } diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/linter/config.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/linter/config.go index a5b98413..2386220d 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/linter/config.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/linter/config.go @@ -185,15 +185,18 @@ func Replacement[T any](replacement string, mgr func(T) any, data T) func(*Depre encoder := yaml.NewEncoder(buf) encoder.SetIndent(2) + linters := map[string]any{ + "enable": []string{d.Replacement}, + } + + replacementSettings := mgr(data) + + if replacementSettings != nil { + linters["settings"] = map[string]any{d.Replacement: replacementSettings} + } + suggestion := map[string]any{ - "linters": map[string]any{ - "enable": []string{ - d.Replacement, - }, - "settings": map[string]any{ - d.Replacement: mgr(data), - }, - }, + "linters": linters, } err := encoder.Encode(suggestion) diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/lintersdb/builder_linter.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/lintersdb/builder_linter.go index 44eadecc..d2b6ac04 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/lintersdb/builder_linter.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/lintersdb/builder_linter.go @@ -9,6 +9,7 @@ import ( "github.com/golangci/golangci-lint/v2/pkg/golinters/bidichk" "github.com/golangci/golangci-lint/v2/pkg/golinters/bodyclose" "github.com/golangci/golangci-lint/v2/pkg/golinters/canonicalheader" + "github.com/golangci/golangci-lint/v2/pkg/golinters/clickhouselint" "github.com/golangci/golangci-lint/v2/pkg/golinters/containedctx" "github.com/golangci/golangci-lint/v2/pkg/golinters/contextcheck" "github.com/golangci/golangci-lint/v2/pkg/golinters/copyloopvar" @@ -161,7 +162,7 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithSince("v1.43.0"). WithURL("https://github.com/breml/bidichk"), - linter.NewConfig(bodyclose.New()). + linter.NewConfig(bodyclose.New(&cfg.Linters.Settings.BodyClose)). WithSince("v1.18.0"). WithLoadForGoAnalysis(). WithURL("https://github.com/timakin/bodyclose"), @@ -172,6 +173,11 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithAutoFix(). WithURL("https://github.com/lasiar/canonicalheader"), + linter.NewConfig(clickhouselint.New()). + WithSince("v2.12.0"). + WithLoadForGoAnalysis(). + WithURL("https://github.com/ClickHouse/clickhouse-go-linter"), + linter.NewConfig(containedctx.New()). WithSince("v1.44.0"). WithLoadForGoAnalysis(). @@ -251,6 +257,13 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { linter.NewConfig(exhaustruct.New(&cfg.Linters.Settings.Exhaustruct)). WithSince("v1.46.0"). + DeprecatedWarning("new major version.", "v2.13.0", + linter.Replacement("exhaustruct_v5", nil, &cfg.Linters.Settings.Exhaustruct)). + WithLoadForGoAnalysis(). + WithURL("https://github.com/GaijinEntertainment/go-exhaustruct"), + + linter.NewConfig(exhaustruct.NewV5(&cfg.Linters.Settings.Exhaustructv5)). + WithSince("v2.13.0"). WithLoadForGoAnalysis(). WithURL("https://github.com/GaijinEntertainment/go-exhaustruct"), @@ -367,7 +380,7 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { linter.NewConfig(golines.New(&cfg.Linters.Settings.GoLines)). WithSince("v2.0.0"). WithAutoFix(). - WithURL("https://github.com/segmentio/golines"), + WithURL("https://github.com/golangci/golines"), linter.NewConfig(goheader.New(&cfg.Linters.Settings.Goheader, placeholderReplacer)). WithSince("v1.28.0"). @@ -385,6 +398,7 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { linter.NewConfig(modernize.New(&cfg.Linters.Settings.Modernize)). WithSince("v2.6.0"). + WithAutoFix(). WithLoadForGoAnalysis(). WithURL("https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize"), @@ -394,6 +408,12 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { linter.NewConfig(gomodguard.New(&cfg.Linters.Settings.Gomodguard)). WithSince("v1.25.0"). + DeprecatedWarning("new major version.", "v2.12.0", + linter.Replacement("gomodguard_v2", gomodguard.Migration, &cfg.Linters.Settings.Gomodguard)). + WithURL("https://github.com/ryancurrah/gomodguard"), + + linter.NewConfig(gomodguard.NewV2(&cfg.Linters.Settings.Gomodguardv2)). + WithSince("v2.12.0"). WithURL("https://github.com/ryancurrah/gomodguard"), linter.NewConfig(goprintffuncname.New()). @@ -593,9 +613,9 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { linter.NewConfig(rowserrcheck.New(&cfg.Linters.Settings.RowsErrCheck)). WithSince("v1.23.0"). WithLoadForGoAnalysis(). - WithURL("https://github.com/jingyugao/rowserrcheck"), + WithURL("https://github.com/golangci/rowserrcheck"), - linter.NewConfig(sloglint.New(&cfg.Linters.Settings.SlogLint)). + linter.NewConfig(sloglint.New(&cfg.Linters.Settings.Sloglint)). WithSince("v1.55.0"). WithLoadForGoAnalysis(). WithAutoFix(). diff --git a/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/lintersdb/builder_plugin_go.go b/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/lintersdb/builder_plugin_go.go index 005ca616..61105da4 100644 --- a/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/lintersdb/builder_plugin_go.go +++ b/vendor/github.com/golangci/golangci-lint/v2/pkg/lint/lintersdb/builder_plugin_go.go @@ -92,7 +92,9 @@ func (b *PluginGoBuilder) getAnalyzerPlugin(cfg *config.Config, path string, set path = filepath.Join(basePath, path) } + //nolint:staticcheck,nolintlint // Ignore because the implementation on Windows returns nil plug, err := plugin.Open(path) + //nolint:staticcheck,nolintlint // Ignore because the implementation on Windows returns nil if err != nil { return nil, err } @@ -106,7 +108,9 @@ func (b *PluginGoBuilder) getAnalyzerPlugin(cfg *config.Config, path string, set } func (b *PluginGoBuilder) lookupPlugin(plug *plugin.Plugin, settings any) ([]*analysis.Analyzer, error) { + //nolint:staticcheck,nolintlint // Ignore because the implementation on Windows returns nil symbol, err := plug.Lookup("New") + //nolint:staticcheck,nolintlint // Ignore because the implementation on Windows returns nil if err != nil { analyzers, errP := b.lookupAnalyzerPlugin(plug) if errP != nil { @@ -126,7 +130,9 @@ func (b *PluginGoBuilder) lookupPlugin(plug *plugin.Plugin, settings any) ([]*an } func (b *PluginGoBuilder) lookupAnalyzerPlugin(plug *plugin.Plugin) ([]*analysis.Analyzer, error) { + //nolint:staticcheck,nolintlint // Ignore because the implementation on Windows returns nil symbol, err := plug.Lookup("AnalyzerPlugin") + //nolint:staticcheck,nolintlint // Ignore because the implementation on Windows returns nil if err != nil { return nil, err } diff --git a/vendor/github.com/jingyugao/rowserrcheck/LICENSE b/vendor/github.com/golangci/rowserrcheck/LICENSE similarity index 100% rename from vendor/github.com/jingyugao/rowserrcheck/LICENSE rename to vendor/github.com/golangci/rowserrcheck/LICENSE diff --git a/vendor/github.com/jingyugao/rowserrcheck/passes/rowserr/rowserr.go b/vendor/github.com/golangci/rowserrcheck/passes/rowserr/rowserr.go similarity index 82% rename from vendor/github.com/jingyugao/rowserrcheck/passes/rowserr/rowserr.go rename to vendor/github.com/golangci/rowserrcheck/passes/rowserr/rowserr.go index a142a674..a05382c6 100644 --- a/vendor/github.com/jingyugao/rowserrcheck/passes/rowserr/rowserr.go +++ b/vendor/github.com/golangci/rowserrcheck/passes/rowserr/rowserr.go @@ -3,6 +3,7 @@ package rowserr import ( "go/ast" "go/types" + "slices" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/buildssa" @@ -35,21 +36,22 @@ type runner struct { sqlPkgs []string } -func NewRun(pkgs ...string) func(pass *analysis.Pass) (interface{}, error) { - return func(pass *analysis.Pass) (interface{}, error) { - sqlPkgs := append(pkgs, "database/sql") +func NewRun(pkgs ...string) func(pass *analysis.Pass) (any, error) { + return func(pass *analysis.Pass) (any, error) { + sqlPkgs := slices.Concat(pkgs, []string{"database/sql"}) for _, pkg := range sqlPkgs { r := new(runner) r.sqlPkgs = sqlPkgs r.run(pass, pkg) } + return nil, nil } } // run executes an analysis for the pass. The receiver is passed // by value because this func is called in parallel for different passes. -func (r runner) run(pass *analysis.Pass, pkgPath string) { +func (r *runner) run(pass *analysis.Pass, pkgPath string) { r.pass = pass pssa := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) funcs := pssa.SrcFuncs @@ -65,6 +67,7 @@ func (r runner) run(pass *analysis.Pass, pkgPath string) { // skip checking return } + r.rowsObj = rowsType.Object() if r.rowsObj == nil { // skip checking @@ -88,8 +91,8 @@ func (r runner) run(pass *analysis.Pass, pkgPath string) { // skip if the function is just referenced var isRefFunc bool - for i := 0; i < f.Signature.Results().Len(); i++ { - if types.Identical(f.Signature.Results().At(i).Type(), r.rowsTyp) { + for v := range f.Signature.Results().Variables() { + if types.Identical(v.Type(), r.rowsTyp) { isRefFunc = true } } @@ -114,38 +117,59 @@ func (r *runner) errCallMissing(b *ssa.BasicBlock, i int) (ret bool) { return false } + if call.Referrers() == nil { + return false + } + for _, cRef := range *call.Referrers() { val, ok := r.getRowsVal(cRef) if !ok { continue } - if len(*val.Referrers()) == 0 { - continue + + if val.Referrers() == nil { + return false } + resRefs := *val.Referrers() + + if len(resRefs) == 0 { + continue + } + var errCalled func(resRef ssa.Instruction) bool + errCalled = func(resRef ssa.Instruction) bool { switch resRef := resRef.(type) { case *ssa.Phi: - for _, rf := range *resRef.Referrers() { - if errCalled(rf) { - return true - } + if resRef.Referrers() == nil { + return false + } + + if slices.ContainsFunc(*resRef.Referrers(), errCalled) { + return true } case *ssa.Store: // Call in Closure function + if resRef.Addr.Referrers() == nil { + return false + } + for _, aref := range *resRef.Addr.Referrers() { switch c := aref.(type) { case *ssa.MakeClosure: f := c.Fn.(*ssa.Function) + called := r.isClosureCalled(c) if r.calledInFunc(f, called) { return true } case *ssa.UnOp: - for _, rf := range *c.Referrers() { - if errCalled(rf) { - return true - } + if c.Referrers() == nil { + continue + } + + if slices.ContainsFunc(*c.Referrers(), errCalled) { + return true } } } @@ -153,6 +177,7 @@ func (r *runner) errCallMissing(b *ssa.BasicBlock, i int) (ret bool) { if r.isErrCall(resRef) { return true } + if f, ok := resRef.Call.Value.(*ssa.Function); ok { for _, b := range f.Blocks { for i := range b.Instrs { @@ -163,16 +188,22 @@ func (r *runner) errCallMissing(b *ssa.BasicBlock, i int) (ret bool) { } } case *ssa.FieldAddr: + if resRef.Referrers() == nil { + return false + } + for _, bRef := range *resRef.Referrers() { bOp, ok := r.getBodyOp(bRef) if !ok { continue } - for _, ccall := range *bOp.Referrers() { - if r.isErrCall(ccall) { - return true - } + if bOp.Referrers() == nil { + continue + } + + if slices.ContainsFunc(*bOp.Referrers(), r.isErrCall) { + return true } } } @@ -180,10 +211,8 @@ func (r *runner) errCallMissing(b *ssa.BasicBlock, i int) (ret bool) { return false } - for _, resRef := range resRefs { - if errCalled(resRef) { - return false - } + if slices.ContainsFunc(resRefs, errCalled) { + return false } } @@ -198,11 +227,12 @@ func (r *runner) getCallReturnsRow(instr ssa.Instruction) (*ssa.Call, bool) { res := call.Call.Signature().Results() - for i := 0; i < res.Len(); i++ { - typeToCheck := res.At(i).Type() + for v := range res.Variables() { + typeToCheck := v.Type() if types.Identical(typeToCheck, r.rowsTyp) { return call, true } + if r.rowsInterface != nil && types.Implements(typeToCheck, r.rowsInterface) { return call, true } @@ -217,6 +247,7 @@ func (r *runner) getRowsVal(instr ssa.Instruction) (ssa.Value, bool) { if len(instr.Call.Args) == 1 && types.Identical(instr.Call.Args[0].Type(), r.rowsTyp) { return instr.Call.Args[0], true } + if len(instr.Call.Args) == 1 && r.rowsInterface != nil && types.Implements(instr.Call.Args[0].Type(), r.rowsInterface) { return instr.Call.Args[0], true } @@ -224,6 +255,7 @@ func (r *runner) getRowsVal(instr ssa.Instruction) (ssa.Value, bool) { if types.Identical(instr.Type(), r.rowsTyp) { return instr, true } + if r.rowsInterface != nil && types.Implements(instr.Type(), r.rowsInterface) { return instr, true } @@ -251,6 +283,7 @@ func (r *runner) isErrCall(ccall ssa.Instruction) bool { if ccall.Call.Value != nil && ccall.Call.Value.Name() == errMethod { return true } + if ccall.Call.Method != nil && ccall.Call.Method.Name() == errMethod { return true } @@ -258,6 +291,7 @@ func (r *runner) isErrCall(ccall ssa.Instruction) bool { if ccall.Call.Value != nil && ccall.Call.Value.Name() == errMethod { return true } + if ccall.Call.Method != nil && ccall.Call.Method.Name() == errMethod { return true } @@ -267,6 +301,10 @@ func (r *runner) isErrCall(ccall ssa.Instruction) bool { } func (r *runner) isClosureCalled(c *ssa.MakeClosure) bool { + if c.Referrers() == nil { + return false + } + for _, ref := range *c.Referrers() { switch ref.(type) { case *ssa.Call, *ssa.Defer: @@ -282,7 +320,12 @@ func (r *runner) calledInFunc(f *ssa.Function, called bool) bool { for i, instr := range b.Instrs { switch instr := instr.(type) { case *ssa.UnOp: + if instr.Referrers() == nil { + continue + } + for _, ref := range *instr.Referrers() { + //nolint:nestif // need to be reviewed. if v, ok := ref.(ssa.Value); ok { if vCall, ok := v.(*ssa.Call); ok { if vCall.Call.Value != nil && vCall.Call.Value.Name() == errMethod { @@ -300,5 +343,6 @@ func (r *runner) calledInFunc(f *ssa.Function, called bool) bool { } } } + return false } diff --git a/vendor/github.com/hashicorp/go-version/CHANGELOG.md b/vendor/github.com/hashicorp/go-version/CHANGELOG.md index 6d48174b..81b42315 100644 --- a/vendor/github.com/hashicorp/go-version/CHANGELOG.md +++ b/vendor/github.com/hashicorp/go-version/CHANGELOG.md @@ -1,3 +1,41 @@ +# 1.9.0 (Mar 30, 2026) + +ENHANCEMENTS: + +Support parsing versions with custom prefixes via opt-in option in https://github.com/hashicorp/go-version/pull/79 + +INTERNAL: + +- Bump the github-actions-backward-compatible group across 1 directory with 2 updates in https://github.com/hashicorp/go-version/pull/179 +- Bump the github-actions-breaking group with 4 updates in https://github.com/hashicorp/go-version/pull/180 +- Bump the github-actions-backward-compatible group with 3 updates in https://github.com/hashicorp/go-version/pull/182 +- Update GitHub Actions to trigger on pull requests and update go version in https://github.com/hashicorp/go-version/pull/185 +- Bump actions/upload-artifact from 6.0.0 to 7.0.0 in the github-actions-breaking group across 1 directory in https://github.com/hashicorp/go-version/pull/183 +- Bump the github-actions-backward-compatible group across 1 directory with 2 updates in https://github.com/hashicorp/go-version/pull/186 + +# 1.8.0 (Nov 28, 2025) + +ENHANCEMENTS: + +- Add benchmark test for version.String() in https://github.com/hashicorp/go-version/pull/159 +- Bytes implementation in https://github.com/hashicorp/go-version/pull/161 + +INTERNAL: + +- Add CODEOWNERS file in .github/CODEOWNERS in https://github.com/hashicorp/go-version/pull/145 +- Linting in https://github.com/hashicorp/go-version/pull/151 +- Correct typos in comments in https://github.com/hashicorp/go-version/pull/134 +- Migrate GitHub Actions updates from TSCCR to Dependabot in https://github.com/hashicorp/go-version/pull/155 +- Bump the github-actions-backward-compatible group with 2 updates in https://github.com/hashicorp/go-version/pull/157 +- Update doc reference in README in https://github.com/hashicorp/go-version/pull/135 +- Bump the github-actions-breaking group with 3 updates in https://github.com/hashicorp/go-version/pull/156 +- [Compliance] - PR Template Changes Required in https://github.com/hashicorp/go-version/pull/158 +- Bump actions/cache from 4.2.3 to 4.2.4 in the github-actions-backward-compatible group in https://github.com/hashicorp/go-version/pull/167 +- Bump actions/checkout from 4.2.2 to 5.0.0 in the github-actions-breaking group in https://github.com/hashicorp/go-version/pull/166 +- Bump the github-actions-breaking group across 1 directory with 2 updates in https://github.com/hashicorp/go-version/pull/171 +- [IND-4226] [COMPLIANCE] Update Copyright Headers in https://github.com/hashicorp/go-version/pull/172 +- drop init() in https://github.com/hashicorp/go-version/pull/175 + # 1.7.0 (May 24, 2024) ENHANCEMENTS: diff --git a/vendor/github.com/hashicorp/go-version/README.md b/vendor/github.com/hashicorp/go-version/README.md index 83a8249f..55289602 100644 --- a/vendor/github.com/hashicorp/go-version/README.md +++ b/vendor/github.com/hashicorp/go-version/README.md @@ -34,6 +34,32 @@ if v1.LessThan(v2) { } ``` +#### Version Parsing and Comparison with Prefixes + +The library also supports parsing versions with a custom prefix. +Using the `WithPrefix` option, you can specify a prefix to strip +before parsing the version. + +Use `WithPrefix` when your input strings carry a known release prefix such as +`deployment-`, `controller-`, etc. + +After parsing, the prefix is not part of the canonical version value. This +means the regular comparison methods such as `Compare`, `LessThan`, `Equal`, +and `GreaterThan` compare only the stripped version. If you compare versions +from different prefixes with these methods, the prefixes are ignored. If you +need to reject cross-prefix comparisons, inspect the parsed prefixes before +comparing the versions. + +```go +v1, _ := version.NewVersion("deployment-v1.2.3-beta+metadata", version.WithPrefix("deployment-")) +v2, _ := version.NewVersion("deployment-v1.2.4", version.WithPrefix("deployment-")) + +if v1.LessThan(v2) { + fmt.Printf("%s (%s) is less than %s (%s)\n", v1, v1.Original(), v2, v2.Original()) + // Outputs: 1.2.3-beta+metadata (deployment-v1.2.3-beta+metadata) is less than 1.2.4 (deployment-v1.2.4) +} +``` + #### Version Constraints ```go diff --git a/vendor/github.com/hashicorp/go-version/version.go b/vendor/github.com/hashicorp/go-version/version.go index 17b29732..b95503d3 100644 --- a/vendor/github.com/hashicorp/go-version/version.go +++ b/vendor/github.com/hashicorp/go-version/version.go @@ -49,6 +49,23 @@ const ( `?` ) +// Optional options for NewVersion function. +type options struct { + // If set, this prefix will be trimmed from the version string before parsing. + prefix string +} + +// Option is a functional option for NewVersion. +type Option func(*options) + +// WithPrefix is a functional option that sets a prefix to be removed from the +// version string before parsing. +func WithPrefix(prefix string) Option { + return func(o *options) { + o.prefix = prefix + } +} + // Version represents a single version. type Version struct { metadata string @@ -56,12 +73,36 @@ type Version struct { segments []int64 si int original string + prefix string } -// NewVersion parses the given version and returns a new -// Version. -func NewVersion(v string) (*Version, error) { - return newVersion(v, getVersionRegexp()) +// NewVersion parses the given version and returns a new Version. +// +// Optional parsing behavior can be enabled with Option values such as +// WithPrefix, which validates and strips an expected prefix before parsing. +func NewVersion(v string, opts ...Option) (*Version, error) { + options := &options{} + for _, opt := range opts { + if opt != nil { + opt(options) + } + } + + vToParse := v + if options.prefix != "" { + if !strings.HasPrefix(v, options.prefix) { + return nil, fmt.Errorf("version %q does not have prefix %q", v, options.prefix) + } + vToParse = strings.TrimPrefix(v, options.prefix) + } + + ver, err := newVersion(vToParse, getVersionRegexp()) + if err != nil { + return nil, err + } + ver.prefix = options.prefix + ver.original = v + return ver, nil } // NewSemver parses the given version and returns a new @@ -424,6 +465,11 @@ func (v *Version) Original() string { return v.original } +// Prefix returns the explicit prefix used with WithPrefix, if any. +func (v *Version) Prefix() string { + return v.prefix +} + // UnmarshalText implements encoding.TextUnmarshaler interface. func (v *Version) UnmarshalText(b []byte) error { temp, err := NewVersion(string(b)) diff --git a/vendor/github.com/jgautheron/goconst/README.md b/vendor/github.com/jgautheron/goconst/README.md index 727974d0..61f8e781 100644 --- a/vendor/github.com/jgautheron/goconst/README.md +++ b/vendor/github.com/jgautheron/goconst/README.md @@ -8,6 +8,16 @@ There are obvious benefits to using constants instead of repeating strings, most While this could be considered a beginner mistake, across time, multiple packages and large codebases, some repetition could have slipped in. +### How it works + +goconst detects string (and optionally number) literals that appear multiple times and could be replaced by a constant. + +A few things to keep in mind: + +- **Exact literal matching** — goconst compares complete, unquoted literal values. Repeated substrings inside larger strings are not detected (e.g., a shared prefix across two different string literals will not be reported). +- **`const` declarations are skipped by default** — constant values are only analyzed when `-match-constant` (match strings against existing constants) or `-find-duplicates` (find constants sharing the same value) is enabled. +- **String length is measured in runes**, not bytes, so multi-byte Unicode characters are counted correctly against `-min-length`. + ### Get Started $ go install github.com/jgautheron/goconst/cmd/goconst@latest @@ -22,20 +32,23 @@ Usage: Flags: - -ignore exclude files matching the given regular expression - -ignore-strings exclude strings matching the given regular expression - -ignore-tests exclude tests from the search (default: true) - -min-occurrences report from how many occurrences (default: 2) - -min-length only report strings with the minimum given length (default: 3) - -match-constant look for existing constants matching the strings - -find-duplicates look for constants with identical values - -eval-const-expr enable evaluation of constant expressions (e.g., Prefix + "suffix") - -numbers search also for duplicated numbers - -min minimum value, only works with -numbers - -max maximum value, only works with -numbers - -output output formatting (text or json) - -set-exit-status Set exit status to 2 if any issues are found - -grouped print single line per match, only works with -output text + -ignore exclude files matching the given regular expression + -ignore-strings exclude strings matching the given regular expression + -ignore-tests exclude tests from the search (default: true) + -min-occurrences report from how many occurrences (default: 2) + -min-length only report strings with the minimum given length (default: 3) + -match-constant look for existing constants matching the strings + -find-duplicates look for constants with identical values + -eval-const-expr enable evaluation of constant expressions (e.g., Prefix + "suffix") + -ignore-calls ignore string literals in calls to these functions (comma separated) + -ignore-composite-literals ignore string literals inside composite literals + -ignore-map-keys ignore string literals used as map keys + -numbers search also for duplicated numbers + -min minimum value, only works with -numbers + -max maximum value, only works with -numbers + -output output formatting (text or json) + -set-exit-status Set exit status to 2 if any issues are found + -grouped print single line per match, only works with -output text Examples: @@ -45,6 +58,7 @@ Examples: goconst -numbers -min 60 -max 512 . goconst -min-occurrences 5 $(go list -m -f '{{.Dir}}') goconst -eval-const-expr -match-constant . # Matches constant expressions like Prefix + "suffix" + goconst -ignore-calls slog.Info,slog.Warn,fmt.Errorf ./... # Ignore strings in logging/error calls ``` ### Development diff --git a/vendor/github.com/jgautheron/goconst/api.go b/vendor/github.com/jgautheron/goconst/api.go index 10cece15..454108e7 100644 --- a/vendor/github.com/jgautheron/goconst/api.go +++ b/vendor/github.com/jgautheron/goconst/api.go @@ -12,6 +12,8 @@ import ( // Issue represents a finding of duplicated strings, numbers, or constants. // Each Issue includes the position where it was found, how many times it occurs, // the string itself, and any matching constant name. +// When both test and non-test files are analyzed, OccurrencesCount reflects +// the count within the issue's scope (test or non-test) rather than the global total. type Issue struct { Pos token.Position OccurrencesCount int @@ -45,6 +47,12 @@ type Config struct { FindDuplicates bool // EvalConstExpressions enables evaluation of constant expressions like Prefix + "suffix" EvalConstExpressions bool + // IgnoreFunctions is a list of function names whose string arguments should be ignored. + // Supports direct calls (e.g., "println") and one-level qualified calls (e.g., "slog.Info"). + IgnoreFunctions []string + // IgnoreMapKeys ignores string literals used as keys in map literals + // (e.g., the "key" in map[string]any{"key": "value"}). + IgnoreMapKeys bool } // NewWithIgnorePatterns creates a new instance of the parser with support for multiple ignore patterns. @@ -108,6 +116,14 @@ func RunWithConfig(files []*ast.File, fset *token.FileSet, typeInfo *types.Info, cfg.ExcludeTypes, ) + if len(cfg.IgnoreFunctions) > 0 { + p.SetIgnoreFunctions(cfg.IgnoreFunctions) + } + + if cfg.IgnoreMapKeys { + p.SetIgnoreMapKeys(true) + } + // Pre-allocate slice based on estimated result size expectedIssues := len(files) * 5 // Assuming average of 5 issues per file if expectedIssues > 1000 { @@ -145,13 +161,14 @@ func RunWithConfig(files []*ast.File, fset *token.FileSet, typeInfo *types.Info, wg.Done() }() - // Use empty interned strings for package/file names - // The visitor logic will set these appropriately - emptyStr := InternString("") + pkgName := "" + if f.Name != nil { + pkgName = f.Name.Name + } ast.Walk(&treeVisitor{ fileSet: fset, - packageName: emptyStr, + packageName: InternString(pkgName), p: p, ignoreRegex: p.ignoreStringsRegex, typeInfo: typeInfo, @@ -170,7 +187,8 @@ func RunWithConfig(files []*ast.File, fset *token.FileSet, typeInfo *types.Info, // Create a slice to hold the string keys stringKeys := make([]string, 0, len(p.strs)) - // Create an array of strings to sort for stable output + // Global count is a coarse prefilter; the reporting loop below + // re-applies minOccurrences per scope (test vs non-test). for str := range p.strs { if count := p.stringCount[str]; count >= p.minOccurrences { stringKeys = append(stringKeys, str) @@ -179,69 +197,127 @@ func RunWithConfig(files []*ast.File, fset *token.FileSet, typeInfo *types.Info, sort.Strings(stringKeys) - // Process strings in a predictable order for stable output + // Emit one issue per file where the string appears, so that + // path-based exclusion can independently filter each one without + // suppressing legitimate findings in other files. + // Occurrence counts are scoped: test-file issues report test-file + // counts and non-test issues report non-test counts, preventing + // test-file occurrences from inflating production-file reports. for _, str := range stringKeys { - positions := p.strs[str] + // Copy positions so the sort does not mutate the map value + // under the read lock. + positions := append([]ExtendedPos(nil), p.strs[str]...) if len(positions) == 0 { continue } - // Use the first position as representative - fi := positions[0] + sortPositions(positions) - // Create issue using the counted value to avoid recounting - issue := Issue{ - Pos: fi.Position, - OccurrencesCount: p.stringCount[str], - Str: str, + var nonTestCount, testCount int + for _, pos := range positions { + if strings.HasSuffix(pos.Filename, testSuffix) { + testCount++ + } else { + nonTestCount++ + } } - // Check for matching constants - if len(p.consts) > 0 { + // Resolve matching constants per scope so that non-test issues + // never reference test-only constants (which production code + // cannot use). Test issues may reference any constant. + // Only resolve when MatchWithConstants is enabled; FindDuplicates + // also populates p.consts but should not affect string issues. + var anyMatchingConst, nonTestMatchingConst string + if p.matchConstant { p.constMutex.RLock() - if csts, ok := p.consts[str]; ok && len(csts) > 0 { - // const should be in the same package and exported - issue.MatchingConst = csts[0].Name - } + raw := p.consts[str] p.constMutex.RUnlock() + + if len(raw) > 0 { + // Copy so the sort does not mutate the map value. + csts := append([]ConstType(nil), raw...) + sortConstants(csts) + anyMatchingConst = csts[0].Name + for _, cst := range csts { + if !strings.HasSuffix(cst.Filename, testSuffix) { + nonTestMatchingConst = cst.Name + break + } + } + } } - issueBuffer = append(issueBuffer, issue) - } + seen := make(map[string]bool) + for _, pos := range positions { + if seen[pos.Filename] { + continue + } + seen[pos.Filename] = true - p.stringCountMutex.RUnlock() - p.stringMutex.RUnlock() + isTest := strings.HasSuffix(pos.Filename, testSuffix) - // process duplicate constants - p.constMutex.RLock() + scopeCount := nonTestCount + if isTest { + scopeCount = testCount + } - // Create a new slice for const keys - stringKeys = make([]string, 0, len(p.consts)) + if scopeCount < p.minOccurrences { + continue + } - // Create an array of strings and sort for stable output - for str := range p.consts { - if len(p.consts[str]) > 1 { - stringKeys = append(stringKeys, str) + matchingConst := nonTestMatchingConst + if isTest && matchingConst == "" { + matchingConst = anyMatchingConst + } + + issueBuffer = append(issueBuffer, Issue{ + Pos: pos.Position, + OccurrencesCount: scopeCount, + Str: str, + MatchingConst: matchingConst, + }) } } - sort.Strings(stringKeys) + p.stringCountMutex.RUnlock() + p.stringMutex.RUnlock() - // report an issue for every duplicated const - for _, str := range stringKeys { - positions := p.consts[str] + // Process duplicate constants only when explicitly requested. + // p.consts may also be populated by matchConstant for constant + // matching, but those extra entries should not trigger duplicate reports. + if p.findDuplicates { + p.constMutex.RLock() + + // Report an issue for every duplicated const within the same scope. + // Test and non-test constants are compared independently so that a + // test constant is never flagged as duplicate of a production one. + for _, group := range duplicateConstGroups(p.consts) { + allConsts := group.consts + + var nonTestConsts, testConsts []ConstType + for _, cst := range allConsts { + if strings.HasSuffix(cst.Filename, testSuffix) { + testConsts = append(testConsts, cst) + } else { + nonTestConsts = append(nonTestConsts, cst) + } + } - for i := 1; i < len(positions); i++ { - issueBuffer = append(issueBuffer, Issue{ - Pos: positions[i].Position, - Str: str, - DuplicateConst: positions[0].Name, - DuplicatePos: positions[0].Position, - }) + for _, scopeConsts := range [][]ConstType{nonTestConsts, testConsts} { + sortConstants(scopeConsts) + for i := 1; i < len(scopeConsts); i++ { + issueBuffer = append(issueBuffer, Issue{ + Pos: scopeConsts[i].Position, + Str: group.displayValue, + DuplicateConst: scopeConsts[0].Name, + DuplicatePos: scopeConsts[0].Position, + }) + } + } } - } - p.constMutex.RUnlock() + p.constMutex.RUnlock() + } // Don't return the buffer to pool as the caller now owns it return issueBuffer, nil @@ -253,3 +329,63 @@ func RunWithConfig(files []*ast.File, fset *token.FileSet, typeInfo *types.Info, func Run(files []*ast.File, fset *token.FileSet, typeInfo *types.Info, cfg *Config) ([]Issue, error) { return RunWithConfig(files, fset, typeInfo, cfg) } + +func lessPosition(a, b token.Position) bool { + if a.Filename != b.Filename { + return a.Filename < b.Filename + } + if a.Line != b.Line { + return a.Line < b.Line + } + return a.Column < b.Column +} + +func sortPositions(positions []ExtendedPos) { + sort.Slice(positions, func(i, j int) bool { + return lessPosition(positions[i].Position, positions[j].Position) + }) +} + +func sortConstants(consts []ConstType) { + sort.Slice(consts, func(i, j int) bool { + return lessPosition(consts[i].Position, consts[j].Position) + }) +} + +type duplicateConstGroup struct { + displayValue string + consts []ConstType +} + +func duplicateConstGroups(consts Constants) []duplicateConstGroup { + groups := make(map[string]duplicateConstGroup, len(consts)) + for displayValue, values := range consts { + for _, cst := range values { + key := cst.ValueKey() + if key == "" { + key = displayValue + } + + group := groups[key] + if group.displayValue == "" || displayValue < group.displayValue { + group.displayValue = displayValue + } + group.consts = append(group.consts, cst) + groups[key] = group + } + } + + keys := make([]string, 0, len(groups)) + for key, group := range groups { + if len(group.consts) > 1 { + keys = append(keys, key) + } + } + sort.Strings(keys) + + result := make([]duplicateConstGroup, 0, len(keys)) + for _, key := range keys { + result = append(result, groups[key]) + } + return result +} diff --git a/vendor/github.com/jgautheron/goconst/parser.go b/vendor/github.com/jgautheron/goconst/parser.go index 9505d463..25e267a5 100644 --- a/vendor/github.com/jgautheron/goconst/parser.go +++ b/vendor/github.com/jgautheron/goconst/parser.go @@ -120,6 +120,8 @@ type Parser struct { minLength, minOccurrences int numberMin, numberMax int excludeTypes map[Type]bool + ignoreFunctions map[string]struct{} + ignoreMapKeys bool // Whether to ignore string literals used as map keys maxConcurrency int evalConstExpressions bool // Whether to evaluate constant expressions @@ -264,6 +266,30 @@ func (p *Parser) EnableBatchProcessing(batchSize int) { } } +// SetIgnoreFunctions configures which function calls should have their string +// arguments ignored. Supports direct calls (e.g., "println") and one-level +// qualified calls (e.g., "slog.Info", "fmt.Errorf"). +func (p *Parser) SetIgnoreFunctions(names []string) { + if len(names) == 0 { + p.ignoreFunctions = nil + return + } + m := make(map[string]struct{}, len(names)) + for _, name := range names { + name = strings.TrimSpace(name) + if name != "" { + m[name] = struct{}{} + } + } + p.ignoreFunctions = m +} + +// SetIgnoreMapKeys configures whether string literals used as keys in map +// literals should be ignored. The corresponding values are still reported. +func (p *Parser) SetIgnoreMapKeys(ignore bool) { + p.ignoreMapKeys = ignore +} + // ParseTree will search the given path for occurrences that could be moved into constants. // If "..." is appended, the search will be recursive. // @@ -308,6 +334,7 @@ func (p *Parser) parseTreeConcurrent(rootPath string, recursive bool) (Strings, // run type checker info := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), } chkConfig := &types.Config{ @@ -419,6 +446,7 @@ func (p *Parser) parseTreeConcurrent(rootPath string, recursive bool) (Strings, // Type checking must be performed serially to avoid data races. info := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), } chkConfig := &types.Config{ @@ -665,6 +693,7 @@ func (p *Parser) parseTreeBatched(rootPath string, recursive bool) (Strings, Con // Type check -- must be processed serially to avoid data races info := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), } chkConfig := &types.Config{ @@ -855,6 +884,13 @@ type ConstType struct { // Interned strings to reduce memory usage Name string packageName string + valueKey string +} + +// ValueKey returns the internal comparison key used to distinguish constants +// whose display values may be approximate, such as high-precision numbers. +func (c ConstType) ValueKey() string { + return c.valueKey } // ExtendedPos extends token.Position with package information. @@ -881,4 +917,7 @@ const ( Return // Call represents a string passed as an argument to a function call (e.g., f("foo")) Call + // CompositeLit represents a string inside a composite literal + // (e.g., []string{"foo"}, map[string]string{"k": "v"}, MyStruct{Field: "foo"}) + CompositeLit ) diff --git a/vendor/github.com/jgautheron/goconst/visitor.go b/vendor/github.com/jgautheron/goconst/visitor.go index 350e3ae6..4c3fba69 100644 --- a/vendor/github.com/jgautheron/goconst/visitor.go +++ b/vendor/github.com/jgautheron/goconst/visitor.go @@ -8,6 +8,7 @@ import ( "regexp" "strconv" "strings" + "unicode/utf8" ) // treeVisitor is used to walk the AST and find strings that could be constants. @@ -17,6 +18,12 @@ type treeVisitor struct { packageName string p *Parser ignoreRegex *regexp.Regexp + + // skipNodes holds map-key expression subtrees to skip entirely while + // walking, used by -ignore-map-keys for keys that are not plain literals + // (e.g. NamedString("key")). Populated per file; the walk is single + // threaded so no synchronization is needed. + skipNodes map[ast.Node]struct{} } // Visit browses the AST tree for strings that could be potentially @@ -27,6 +34,12 @@ func (v *treeVisitor) Visit(node ast.Node) ast.Visitor { return v } + // Prune map-key expression subtrees flagged by -ignore-map-keys so their + // string literals are never recorded (e.g. the "key" in K("key")). + if _, skip := v.skipNodes[node]; skip { + return nil + } + // A single case with "ast.BasicLit" would be much easier // but then we wouldn't be able to tell in which context // the string is defined (could be a constant definition). @@ -42,21 +55,42 @@ func (v *treeVisitor) Visit(node ast.Node) ast.Visitor { for _, spec := range t.Specs { val := spec.(*ast.ValueSpec) + if v.typeInfo != nil && v.p.evalConstExpressions { + if len(v.typeInfo.Defs) > 0 { + addedFromDefs := false + for _, name := range val.Names { + obj, ok := v.typeInfo.Defs[name].(*types.Const) + if !ok || obj.Val() == nil || !v.isSupportedKind(obj.Val().Kind()) { + continue + } + + displayValue, valueKey := constValueStrings(obj.Val()) + v.addConst(name.Name, displayValue, name.Pos(), valueKey) + addedFromDefs = true + } + if addedFromDefs || len(val.Values) == 0 { + continue + } + } + } + for i, str := range val.Values { if v.typeInfo != nil && v.p.evalConstExpressions { typedVal, ok := v.typeInfo.Types[str] - if !ok || !v.isSupportedKind(typedVal.Value.Kind()) { + if !ok || typedVal.Value == nil || !v.isSupportedKind(typedVal.Value.Kind()) { continue } - v.addConst(val.Names[i].Name, typedVal.Value.String(), str.Pos()) - } else { - lit, ok := str.(*ast.BasicLit) - if !ok || !v.isSupported(lit.Kind) { - continue - } - v.addConst(val.Names[i].Name, lit.Value, val.Names[i].Pos()) + displayValue, valueKey := constValueStrings(typedVal.Value) + v.addConst(val.Names[i].Name, displayValue, str.Pos(), valueKey) + continue } + + lit, ok := str.(*ast.BasicLit) + if !ok || !v.isSupported(lit.Kind) { + continue + } + v.addConst(val.Names[i].Name, lit.Value, val.Names[i].Pos()) } } @@ -110,17 +144,115 @@ func (v *treeVisitor) Visit(node ast.Node) ast.Visitor { // fn("http://") case *ast.CallExpr: - for _, item := range t.Args { - lit, ok := item.(*ast.BasicLit) - if ok && v.isSupported(lit.Kind) { - v.addString(lit.Value, lit.Pos(), Call) + if !v.shouldIgnoreCall(t) { + for _, item := range t.Args { + lit, ok := item.(*ast.BasicLit) + if ok && v.isSupported(lit.Kind) { + v.addString(lit.Value, lit.Pos(), Call) + } } } + + // []string{"foo"}, map[string]string{"k": "v"}, struct{A string}{A: "foo"} + case *ast.CompositeLit: + // isMap is only consulted for pruning expression keys, which matters + // only when -ignore-map-keys is set. + isMap := false + if v.p.ignoreMapKeys { + isMap = v.isMapLiteral(t) + } + for _, item := range t.Elts { + v.addCompositeLiteralElement(item, isMap) + } } return v } +func constValueStrings(val constant.Value) (string, string) { + if val.Kind() == constant.String { + return val.ExactString(), "string:" + constant.StringVal(val) + } + return val.String(), val.Kind().String() + ":" + val.ExactString() +} + +// isMapLiteral reports whether a composite literal is a map. It prefers type +// information (which resolves named map types and elided nested literals) and +// falls back to the syntactic type when none is available (e.g. the CLI path, +// where only explicit map[...]... literals can be recognized). +func (v *treeVisitor) isMapLiteral(t *ast.CompositeLit) bool { + if v.typeInfo != nil { + if tv, ok := v.typeInfo.Types[t]; ok && tv.Type != nil { + _, isMap := tv.Type.Underlying().(*types.Map) + return isMap + } + } + _, ok := t.Type.(*ast.MapType) + return ok +} + +func (v *treeVisitor) addCompositeLiteralElement(node ast.Expr, isMap bool) { + if lit, ok := node.(*ast.BasicLit); ok && v.isSupported(lit.Kind) { + v.addString(lit.Value, lit.Pos(), CompositeLit) + return + } + + kv, ok := node.(*ast.KeyValueExpr) + if !ok { + return + } + + if keyLit, ok := kv.Key.(*ast.BasicLit); ok { + // Direct literal key. A string literal can only be a map key (struct + // keys are identifiers, array indices are integers), so it is dropped + // when requested with no type information; numeric keys are out of + // scope and kept. + if v.isSupported(keyLit.Kind) && (!v.p.ignoreMapKeys || keyLit.Kind != token.STRING) { + v.addString(keyLit.Value, keyLit.Pos(), CompositeLit) + } + } else if v.p.ignoreMapKeys && isMap { + // Key is an expression (e.g. NamedString("key")); any string literal it + // contains is part of the map key, so skip the whole subtree. Gated on + // isMap so array indices such as [10]int{int(2): 1} are left alone. + v.markSkip(kv.Key) + } + + if valueLit, ok := kv.Value.(*ast.BasicLit); ok && v.isSupported(valueLit.Kind) { + v.addString(valueLit.Value, valueLit.Pos(), CompositeLit) + } +} + +// markSkip flags an AST node so the walk prunes it and its subtree. +func (v *treeVisitor) markSkip(node ast.Node) { + if v.skipNodes == nil { + v.skipNodes = make(map[ast.Node]struct{}) + } + v.skipNodes[node] = struct{}{} +} + +// shouldIgnoreCall returns true if the call expression matches a function +// name in the ignoreFunctions set. Supports direct calls (e.g., "println") +// and one-level qualified calls (e.g., "slog.Info"). +func (v *treeVisitor) shouldIgnoreCall(call *ast.CallExpr) bool { + if len(v.p.ignoreFunctions) == 0 { + return false + } + var name string + switch fn := call.Fun.(type) { + case *ast.Ident: + name = fn.Name + case *ast.SelectorExpr: + if ident, ok := fn.X.(*ast.Ident); ok { + name = ident.Name + "." + fn.Sel.Name + } + } + if name == "" { + return false + } + _, found := v.p.ignoreFunctions[name] + return found +} + // addString adds a string in the map along with its position in the tree. func (v *treeVisitor) addString(str string, pos token.Pos, typ Type) { // Early type exclusion check @@ -153,7 +285,7 @@ func (v *treeVisitor) addString(str string, pos token.Pos, typ Type) { } // Early length check - if len(unquotedStr) == 0 || len(unquotedStr) < v.p.minLength { + if len(unquotedStr) == 0 || utf8.RuneCountInString(unquotedStr) < v.p.minLength { return } @@ -175,33 +307,25 @@ func (v *treeVisitor) addString(str string, pos token.Pos, typ Type) { // Use interned string to reduce memory usage - identical strings share the same memory internedStr := InternString(unquotedStr) - // Update the count first, this is faster than appending to slices - count := v.p.IncrementStringCount(internedStr) + // Update the count for fast threshold checks in ProcessResults + v.p.IncrementStringCount(internedStr) - // Only continue if we're still adding the position to the map - // or if count has reached threshold - if count == 1 || count == v.p.minOccurrences { - // Lock to safely update the shared map - v.p.stringMutex.Lock() - defer v.p.stringMutex.Unlock() + // Record every occurrence so that position lists and display counts stay accurate + v.p.stringMutex.Lock() + defer v.p.stringMutex.Unlock() - _, exists := v.p.strs[internedStr] - if !exists { - v.p.strs[internedStr] = make([]ExtendedPos, 0, v.p.minOccurrences) // Preallocate with expected size - } - - // Create an optimized position record - newPos := ExtendedPos{ - packageName: InternString(v.packageName), // Intern the package name to reduce memory - Position: v.fileSet.Position(pos), - } - - v.p.strs[internedStr] = append(v.p.strs[internedStr], newPos) + if _, exists := v.p.strs[internedStr]; !exists { + v.p.strs[internedStr] = make([]ExtendedPos, 0, v.p.minOccurrences) } + + v.p.strs[internedStr] = append(v.p.strs[internedStr], ExtendedPos{ + packageName: InternString(v.packageName), + Position: v.fileSet.Position(pos), + }) } // addConst adds a const in the map along with its position in the tree. -func (v *treeVisitor) addConst(name string, val string, pos token.Pos) { +func (v *treeVisitor) addConst(name string, val string, pos token.Pos, valueKey ...string) { // Early filtering using the same criteria as for strings var unquotedVal string if strings.HasPrefix(val, `"`) || strings.HasPrefix(val, "`") { @@ -224,7 +348,7 @@ func (v *treeVisitor) addConst(name string, val string, pos token.Pos) { } // Skip constants with values that would be filtered anyway - if len(unquotedVal) < v.p.minLength { + if utf8.RuneCountInString(unquotedVal) < v.p.minLength { return } @@ -236,16 +360,23 @@ func (v *treeVisitor) addConst(name string, val string, pos token.Pos) { internedVal := InternString(unquotedVal) internedName := InternString(name) internedPkg := InternString(v.packageName) + internedKey := internedVal + if len(valueKey) > 0 && valueKey[0] != "" { + internedKey = InternString(valueKey[0]) + } // Lock to safely update the shared map v.p.constMutex.Lock() defer v.p.constMutex.Unlock() - // track this const if this is a new const, or if we are searching for duplicate consts - if _, ok := v.p.consts[internedVal]; !ok || v.p.findDuplicates { + // Collect the constant when it is the first with this value, when + // duplicate detection needs all of them, or when constant matching + // needs all of them to pick the best per scope. + if _, ok := v.p.consts[internedVal]; !ok || v.p.findDuplicates || v.p.matchConstant { v.p.consts[internedVal] = append(v.p.consts[internedVal], ConstType{ Name: internedName, packageName: internedPkg, + valueKey: internedKey, Position: v.fileSet.Position(pos), }) } diff --git a/vendor/github.com/kisielk/errcheck/errcheck/errcheck.go b/vendor/github.com/kisielk/errcheck/errcheck/errcheck.go index 325aeec9..05e91955 100644 --- a/vendor/github.com/kisielk/errcheck/errcheck/errcheck.go +++ b/vendor/github.com/kisielk/errcheck/errcheck/errcheck.go @@ -262,7 +262,7 @@ type visitor struct { // If the call does not include a selector (like if it is a plain "f()" function call) // then the final return value will be false. func (v *visitor) selectorAndFunc(call *ast.CallExpr) (*ast.SelectorExpr, *types.Func, bool) { - sel, ok := call.Fun.(*ast.SelectorExpr) + sel, ok := baseCallExpr(call.Fun).(*ast.SelectorExpr) if !ok { return nil, nil, false } @@ -420,14 +420,15 @@ func (v *visitor) ignoreCall(call *ast.CallExpr) bool { // Currently only supports simple expressions: // 1. f() // 2. x.y.f() + // 3. x.y[T]() or x.y[T1, T2]() var id *ast.Ident - switch exp := call.Fun.(type) { + switch exp := baseCallExpr(call.Fun).(type) { case *ast.Ident: id = exp case *ast.SelectorExpr: id = exp.Sel default: - // eg: *ast.SliceExpr, *ast.IndexExpr + // eg: *ast.SliceExpr } if id == nil { @@ -450,6 +451,24 @@ func (v *visitor) ignoreCall(call *ast.CallExpr) bool { return false } +// baseCallExpr returns the underlying function expression for a call. Type +// arguments and parentheses wrap the selector/name in additional AST nodes, so +// matching call.Fun directly would miss forms like errors.AsType[T](err). +func baseCallExpr(fun ast.Expr) ast.Expr { + for { + switch f := fun.(type) { + case *ast.IndexExpr: + fun = f.X + case *ast.IndexListExpr: + fun = f.X + case *ast.ParenExpr: + fun = f.X + default: + return fun + } + } +} + // nonVendoredPkgPath returns the unvendored version of the provided package // path (or returns the provided path if it does not represent a vendored // path). diff --git a/vendor/github.com/kisielk/errcheck/errcheck/excludes.go b/vendor/github.com/kisielk/errcheck/errcheck/excludes.go index 3e28a2fd..1ccc631f 100644 --- a/vendor/github.com/kisielk/errcheck/errcheck/excludes.go +++ b/vendor/github.com/kisielk/errcheck/errcheck/excludes.go @@ -50,6 +50,9 @@ var DefaultExcludedSymbols = []string{ // hash "(hash.Hash).Write", + "(*crypto/sha3.SHA3).Write", + "(*crypto/sha3.SHAKE).Read", + "(*crypto/sha3.SHAKE).Write", // hash/maphash "(*hash/maphash.Hash).Write", diff --git a/vendor/github.com/ldez/gomoddirectives/.golangci.yml b/vendor/github.com/ldez/gomoddirectives/.golangci.yml index 8eb11ff0..90c529bf 100644 --- a/vendor/github.com/ldez/gomoddirectives/.golangci.yml +++ b/vendor/github.com/ldez/gomoddirectives/.golangci.yml @@ -29,6 +29,7 @@ linters: - varnamelen - wrapcheck - wsl # deprecated + - gomodguard # deprecated settings: depguard: @@ -51,6 +52,7 @@ linters: goconst: min-len: 3 min-occurrences: 3 + ignore-tests: true gocritic: disabled-checks: - sloppyReassign diff --git a/vendor/github.com/ldez/gomoddirectives/gomoddirectives.go b/vendor/github.com/ldez/gomoddirectives/gomoddirectives.go index ae416c17..32c7b8d9 100644 --- a/vendor/github.com/ldez/gomoddirectives/gomoddirectives.go +++ b/vendor/github.com/ldez/gomoddirectives/gomoddirectives.go @@ -52,6 +52,7 @@ func (r Result) String() string { // Options the analyzer options. type Options struct { + ReplaceAllowAll bool ReplaceAllowList []string ReplaceAllowLocal bool ExcludeForbidden bool @@ -252,6 +253,10 @@ func checkReplaceDirectives(file *modfile.File, opts Options) []Result { } func checkReplaceDirective(opts Options, r *modfile.Replace) string { + if opts.ReplaceAllowAll { + return "" + } + if isLocal(r) { if opts.ReplaceAllowLocal { return "" diff --git a/vendor/github.com/ldez/gomoddirectives/readme.md b/vendor/github.com/ldez/gomoddirectives/readme.md index 52c6a4cb..ea37304c 100644 --- a/vendor/github.com/ldez/gomoddirectives/readme.md +++ b/vendor/github.com/ldez/gomoddirectives/readme.md @@ -26,7 +26,11 @@ linters: # Default: [] replace-allow-list: - launchpad.net/gocheck - + + # Allow all `replace` directives. + # Default: false + replace-allow-all: true + # Allow to not explain why the version has been retracted in the `retract` directives. # Default: false retract-allow-no-explanation: true @@ -85,6 +89,8 @@ Flags: List of allowed replace directives -local Allow local replace directives + -all-replace + Allow all replace directives -retract-no-explanation Allow to use retract directives without explanation -tool @@ -120,6 +126,7 @@ retract ( - Ban all `replace` directives. - Allow only local `replace` directives. - Allow only some `replace` directives. +- Allow all `replace` directives. - Detect duplicated `replace` directives. - Detect identical `replace` directives. diff --git a/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md b/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md index 84f9c7b2..f349b335 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md +++ b/vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md @@ -6,8 +6,42 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm The format of this file is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), but only releases after v1.0.3 properly adhere to it. +## [Unreleased] + +## [1.4.1] - 2026-08-02 +### Fixed +- Corrected `D50ToD65` to use the CSS Color 4 matrix inverse of `D65ToD50` (#85). + +## [1.4.0] - 2026-03-28 +### Added +- Constructors, decomposers, and blend functions for the CSS Color Level 4 wide-gamut RGB color spaces `DisplayP3`, `A98Rgb`, `ProPhotoRgb`, and `Rec2020` (#81) +- `XyzD50`, `Color.XyzD50`, `D50ToD65`, and `D65ToD50` for working with D50-based color spaces (#81) +- `HexColor` now implements `fmt.Stringer` + +## [1.3.0] - 2025-09-08 +### Added +- `BlendLinearRgb` (#50) +- `DistanceRiemersma` (#52) +- Introduce a function for sorting colors (#57) +- YAML marshal/unmarshal support (#63) +- Add support for OkLab and OkLch (#66) +- Functions that use randomness now support specifying a custom source (#73) +- Functions BlendOkLab and BlendOkLch (#70) + +## Changed +- `Hex()` parsing is much faster (#78). However, it doesn't tolerate hex codes with alpha anymore (previously ignoring the alpha was unintentional). + +### Fixed +- Fix bug when doing HSV/HCL blending between a gray color and non-gray color (#60) +- Docs for HSV/HSL were updated to note that hue 360 is not allowed (#71) + +### Deprecated +- `DistanceLinearRGB` is deprecated for the name `DistanceLinearRgb` which is more in-line with the rest of the library + ## [1.2.0] - 2021-01-27 +This is the same as the v1.1.0 tag. + ### Added - HSLuv and HPLuv color spaces (#41, #51) - CIE LCh(uv) color space, called `LuvLCh` in code (#51) diff --git a/vendor/github.com/lucasb-eyer/go-colorful/README.md b/vendor/github.com/lucasb-eyer/go-colorful/README.md index 8b9bd499..1da19f70 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/README.md +++ b/vendor/github.com/lucasb-eyer/go-colorful/README.md @@ -1,6 +1,7 @@ go-colorful =========== +[![Go Reference](https://pkg.go.dev/badge/github.com/lucasb-eyer/go-colorful.svg)](https://pkg.go.dev/github.com/lucasb-eyer/go-colorful) [![go reportcard](https://goreportcard.com/badge/github.com/lucasb-eyer/go-colorful)](https://goreportcard.com/report/github.com/lucasb-eyer/go-colorful) A library for playing with colors in Go. Supports Go 1.13 onwards. @@ -29,10 +30,12 @@ Go-Colorful stores colors in RGB and provides methods from converting these to v - **CIE-xyY:** encodes chromacity in x and y and luminance in Y, all in [0..1] - **CIE-L\*a\*b\*:** A *perceptually uniform* color space, i.e. distances are meaningful. L\* in [0..1] and a\*, b\* almost in [-1..1]. - **CIE-L\*u\*v\*:** Very similar to CIE-L\*a\*b\*, there is [no consensus](http://en.wikipedia.org/wiki/CIELUV#Historical_background) on which one is "better". -- **CIE-L\*C\*h° (HCL):** This is generally the [most useful](http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/) one; CIE-L\*a\*b\* space in polar coordinates, i.e. a *better* HSV. H° is in [0..360], C\* almost in [-1..1] and L\* as in CIE-L\*a\*b\*. -- **CIE LCh(uv):** Called `LuvLCh` in code, this is a cylindrical transformation of the CIE-L\*u\*v\* color space. Like HCL above: H° is in [0..360], C\* almost in [-1..1] and L\* as in CIE-L\*u\*v\*. +- **CIE-L\*C\*h° (HCL):** This is generally the [most useful](http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/) one; CIE-L\*a\*b\* space in polar coordinates, i.e. a *better* HSV. H° is in [0..360], C\* almost in [0..1] and L\* as in CIE-L\*a\*b\*. +- **CIE LCh(uv):** Called `LuvLCh` in code, this is a cylindrical transformation of the CIE-L\*u\*v\* color space. Like HCL above: H° is in [0..360], C\* almost in [0..1] and L\* as in CIE-L\*u\*v\*. - **HSLuv:** The better alternative to HSL, see [here](https://www.hsluv.org/) and [here](https://www.kuon.ch/post/2020-03-08-hsluv/). Hue in [0..360], Saturation and Luminance in [0..1]. -- **HPLuv:** A variant of HSLuv. The color space is smoother, but only pastel colors can be included. Because the valid colors are limited, it's easy to get invalid Saturation values way above 1.0, indicating the color can't be represented in HPLuv beccause it's not pastel. +- **HPLuv:** A variant of HSLuv. The color space is smoother, but only pastel colors can be included. Because the valid colors are limited, it's easy to get invalid Saturation values way above 1.0, indicating the color can't be represented in HPLuv because it's not pastel. +- **Oklab:** A perceptual color space by Björn Ottosson that improves on CIE-L\*a\*b\* with better perceptual uniformity, especially for blue hues. L in [0..1], a and b roughly in [-0.5..0.5]. See [Oklab](https://bottosson.github.io/posts/oklab/). +- **Oklch:** The cylindrical (polar) representation of Oklab, similar to HCL. L in [0..1], C roughly in [0..0.5], h° in [0..360]. For the colorspaces where it makes sense (XYZ, Lab, Luv, HCl), the [D65](http://en.wikipedia.org/wiki/Illuminant_D65) is used as reference white @@ -52,14 +55,6 @@ Nice, but what's it useful for? - Generating random colors under some constraints (e.g. colors of the same shade, or shades of one color.) - Generating gorgeous random palettes with distinct colors of a same temperature. -What not (yet)? -=============== -There are a few features which are currently missing and might be useful. -I just haven't implemented them yet because I didn't have the need for it. -Pull requests welcome. - -- Sorting colors (potentially using above mentioned distances) - So which colorspace should I use? ================================= It depends on what you want to do. I think the folks from *I want hue* are @@ -103,6 +98,8 @@ c = colorful.Xyy(0.219895, 0.221839, 0.190837) c = colorful.Lab(0.507850, 0.040585,-0.370945) c = colorful.Luv(0.507849,-0.194172,-0.567924) c = colorful.Hcl(276.2440, 0.373160, 0.507849) +c = colorful.OkLab(0.577227, -0.021391, -0.104541) +c = colorful.OkLch(0.577227, 0.106707, 258.435657) fmt.Printf("RGB values: %v, %v, %v", c.R, c.G, c.B) ``` @@ -116,6 +113,8 @@ x, y, Y := c.Xyy() l, a, b := c.Lab() l, u, v := c.Luv() h, c, l := c.Hcl() +l, a, b = c.OkLab() +l, c, h = c.OkLch() ``` Note that, because of Go's unfortunate choice of requiring an initial uppercase, @@ -139,7 +138,7 @@ alpha colors, this means the RGB values are lost (set to 0) and it's impossible to recover them. In such a case `MakeColor` will return `false` as its second value. ### Comparing colors -In the RGB color space, the Euclidian distance between colors *doesn't* correspond +In the RGB color space, the Euclidean distance between colors *doesn't* correspond to visual/perceptual distance. This means that two pairs of colors which have the same distance in RGB space can look much further apart. This is fixed by the CIE-L\*a\*b\*, CIE-L\*u\*v\* and CIE-L\*C\*h° color spaces. @@ -197,7 +196,7 @@ it only if you really know what you're doing. It will eat your cat. Blending is highly connected to distance, since it basically "walks through" the colorspace thus, if the colorspace maps distances well, the walk is "smooth". -Colorful comes with blending functions in RGB, HSV and any of the LAB spaces. +Colorful comes with blending functions in RGB, HSV, Oklab, Oklch, and any of the CIE-LAB spaces. Of course, you'd rather want to use the blending functions of the LAB spaces since these spaces map distances well but, just in case, here is an example showing you how the blendings (`#fdffcc` to `#242a42`) are done in the various spaces: @@ -208,7 +207,7 @@ What you see is that HSV is really bad: it adds some green, which is not present in the original colors at all! RGB is much better, but it stays light a little too long. LUV and LAB both hit the right lightness but LAB has a little more color. HCL works in the same vein as HSV (both cylindrical interpolations) but -it does it right in that there is no green appearing and the lighthness changes +it does it right in that there is no green appearing and the lightness changes in a linear manner. While this seems all good, you need to know one thing: When interpolating in any @@ -316,11 +315,11 @@ generating this picture in `doc/colorgens/colorgens.go`. ### Getting random palettes As soon as you need to generate more than one random color, you probably want -them to be distinguishible. Playing against an opponent which has almost the +them to be distinguishable. Playing against an opponent which has almost the same blue as I do is not fun. This is where random palettes can help. These palettes are generated using an algorithm which ensures that all colors -on the palette are as distinguishible as possible. Again, there is a `Fast` +on the palette are as distinguishable as possible. Again, there is a `Fast` method which works in HSV and is less perceptually uniform and a non-`Fast` method which works in CIE spaces. For more theory on `SoftPalette`, check out [I want hue](http://tools.medialab.sciences-po.fr/iwanthue/theory.php). Yet @@ -372,10 +371,18 @@ from top to bottom: `Warm`, `FastWarm`, `Happy`, `FastHappy`, `Soft`, Again, the code used for generating the above image is available as [doc/palettegens/palettegens.go](https://github.com/lucasb-eyer/go-colorful/blob/master/doc/palettegens/palettegens.go). ### Sorting colors -TODO: Sort using dist fn. + +Sorting colors is not a well-defined operation. For example, {dark blue, dark red, light blue, light red} is already sorted if darker colors should precede lighter colors but would need to be re-sorted as {dark red, light red, dark blue, light blue} if longer-wavelength colors should precede shorter-wavelength colors. + +Go-Colorful's `Sorted` function orders a list of colors so as to minimize the average distance between adjacent colors, including between the last and the first. (`Sorted` does not necessarily find the true minimum, only a reasonably close approximation.) The following picture, drawn by [doc/colorsort/colorsort.go](https://github.com/lucasb-eyer/go-colorful/blob/master/doc/colorsort/colorsort.go), illustrates `Sorted`'s behavior: + +![Sorting colors](doc/colorsort/colorsort.png) + +The first row represents the input: a slice of 512 randomly chosen colors. The second row shows the colors sorted in CIE-L\*C\*h° space, ordered first by lightness (L), then by hue angle (h), and finally by chroma (C). Note that distracting pinstripes permeate the colors. Sorting using *any* color space and *any* ordering of the channels yields a similar pinstriped pattern. The third row of the image was sorted using Go-Colorful's `Sorted` function. Although the colors do not appear to be in any particular order, the sequence at least appears smoother than the one sorted by channel. + ### Using linear RGB for computations -There are two methods for transforming RGB<->Linear RGB: a fast and almost precise one, +There are two methods for transforming RGB⟷Linear RGB: a fast and almost precise one, and a slow and precise one. ```go @@ -471,11 +478,12 @@ section above. Who? ==== -This library was developed by Lucas Beyer with contributions from -Bastien Dejean (@baskerville), Phil Kulak (@pkulak) and Christian Muehlhaeuser (@muesli). - -It is now maintained by makeworld (@makeworld-the-better-one). +This library was originally developed by Lucas Beyer, with notable +contributions from Bastien Dejean (@baskerville), Phil Kulak (@pkulak), +Christian Muehlhaeuser (@muesli), Scott Pakin (@spakin), and many others. +See the [contributors list](https://github.com/lucasb-eyer/go-colorful/graphs/contributors) for the full roster. +It is currently maintained by makeworld (@makew0rld). ## License diff --git a/vendor/github.com/lucasb-eyer/go-colorful/colorgens.go b/vendor/github.com/lucasb-eyer/go-colorful/colorgens.go index 2e2e49e1..ac697d65 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/colorgens.go +++ b/vendor/github.com/lucasb-eyer/go-colorful/colorgens.go @@ -2,28 +2,32 @@ package colorful -import ( - "math/rand" -) - // Creates a random dark, "warm" color through a restricted HSV space. -func FastWarmColor() Color { +func FastWarmColorWithRand(rand RandInterface) Color { return Hsv( rand.Float64()*360.0, 0.5+rand.Float64()*0.3, 0.3+rand.Float64()*0.3) } +func FastWarmColor() Color { + return FastWarmColorWithRand(getDefaultGlobalRand()) +} + // Creates a random dark, "warm" color through restricted HCL space. // This is slower than FastWarmColor but will likely give you colors which have // the same "warmness" if you run it many times. -func WarmColor() (c Color) { - for c = randomWarm(); !c.IsValid(); c = randomWarm() { +func WarmColorWithRand(rand RandInterface) (c Color) { + for c = randomWarmWithRand(rand); !c.IsValid(); c = randomWarmWithRand(rand) { } return } -func randomWarm() Color { +func WarmColor() (c Color) { + return WarmColorWithRand(getDefaultGlobalRand()) +} + +func randomWarmWithRand(rand RandInterface) Color { return Hcl( rand.Float64()*360.0, 0.1+rand.Float64()*0.3, @@ -31,23 +35,31 @@ func randomWarm() Color { } // Creates a random bright, "pimpy" color through a restricted HSV space. -func FastHappyColor() Color { +func FastHappyColorWithRand(rand RandInterface) Color { return Hsv( rand.Float64()*360.0, 0.7+rand.Float64()*0.3, 0.6+rand.Float64()*0.3) } +func FastHappyColor() Color { + return FastHappyColorWithRand(getDefaultGlobalRand()) +} + // Creates a random bright, "pimpy" color through restricted HCL space. // This is slower than FastHappyColor but will likely give you colors which // have the same "brightness" if you run it many times. -func HappyColor() (c Color) { - for c = randomPimp(); !c.IsValid(); c = randomPimp() { +func HappyColorWithRand(rand RandInterface) (c Color) { + for c = randomPimpWithRand(rand); !c.IsValid(); c = randomPimpWithRand(rand) { } return } -func randomPimp() Color { +func HappyColor() (c Color) { + return HappyColorWithRand(getDefaultGlobalRand()) +} + +func randomPimpWithRand(rand RandInterface) Color { return Hcl( rand.Float64()*360.0, 0.5+rand.Float64()*0.3, diff --git a/vendor/github.com/lucasb-eyer/go-colorful/colors.go b/vendor/github.com/lucasb-eyer/go-colorful/colors.go index 0d5bffe5..17441a8c 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/colors.go +++ b/vendor/github.com/lucasb-eyer/go-colorful/colors.go @@ -5,6 +5,7 @@ import ( "fmt" "image/color" "math" + "strconv" ) // A color is stored internally using sRGB (standard RGB) values in the range 0-1 @@ -94,15 +95,39 @@ func (c1 Color) DistanceRgb(c2 Color) float64 { return math.Sqrt(sq(c1.R-c2.R) + sq(c1.G-c2.G) + sq(c1.B-c2.B)) } -// DistanceLinearRGB computes the distance between two colors in linear RGB +// DistanceLinearRgb computes the distance between two colors in linear RGB // space. This is not useful for measuring how humans perceive color, but // might be useful for other things, like dithering. -func (c1 Color) DistanceLinearRGB(c2 Color) float64 { +func (c1 Color) DistanceLinearRgb(c2 Color) float64 { r1, g1, b1 := c1.LinearRgb() r2, g2, b2 := c2.LinearRgb() return math.Sqrt(sq(r1-r2) + sq(g1-g2) + sq(b1-b2)) } +// DistanceLinearRGB is deprecated in favour of DistanceLinearRgb. +// They do the exact same thing. +func (c1 Color) DistanceLinearRGB(c2 Color) float64 { + return c1.DistanceLinearRgb(c2) +} + +// DistanceRiemersma is a color distance algorithm developed by Thiadmer Riemersma. +// It uses RGB coordinates, but he claims it has similar results to CIELUV. +// This makes it both fast and accurate. +// +// Sources: +// +// https://www.compuphase.com/cmetric.htm +// https://github.com/lucasb-eyer/go-colorful/issues/52 +func (c1 Color) DistanceRiemersma(c2 Color) float64 { + rAvg := (c1.R + c2.R) / 2.0 + // Deltas + dR := c1.R - c2.R + dG := c1.G - c2.G + dB := c1.B - c2.B + + return math.Sqrt((2+rAvg)*dR*dR + 4*dG*dG + (2+(1-rAvg))*dB*dB) +} + // Check for equality between colors within the tolerance Delta (1/255). func (c1 Color) AlmostEqualRgb(c2 Color) bool { return math.Abs(c1.R-c2.R)+ @@ -112,9 +137,11 @@ func (c1 Color) AlmostEqualRgb(c2 Color) bool { // You don't really want to use this, do you? Go for BlendLab, BlendLuv or BlendHcl. func (c1 Color) BlendRgb(c2 Color, t float64) Color { - return Color{c1.R + t*(c2.R-c1.R), + return Color{ + c1.R + t*(c2.R-c1.R), c1.G + t*(c2.G-c1.G), - c1.B + t*(c2.B-c1.B)} + c1.B + t*(c2.B-c1.B), + } } // Utility used by Hxx color-spaces for interpolating between two angles in [0,360]. @@ -128,9 +155,9 @@ func interp_angle(a0, a1, t float64) float64 { /// HSV /// /////////// // From http://en.wikipedia.org/wiki/HSL_and_HSV -// Note that h is in [0..360] and s,v in [0..1] +// Note that h is in [0..359] and s,v in [0..1] -// Hsv returns the Hue [0..360], Saturation and Value [0..1] of the color. +// Hsv returns the Hue [0..359], Saturation and Value [0..1] of the color. func (col Color) Hsv() (h, s, v float64) { min := math.Min(math.Min(col.R, col.G), col.B) v = math.Max(math.Max(col.R, col.G), col.B) @@ -160,7 +187,7 @@ func (col Color) Hsv() (h, s, v float64) { return } -// Hsv creates a new Color given a Hue in [0..360], a Saturation and a Value in [0..1] +// Hsv creates a new Color given a Hue in [0..359], a Saturation and a Value in [0..1] func Hsv(H, S, V float64) Color { Hp := H / 60.0 C := V * S @@ -198,6 +225,13 @@ func (c1 Color) BlendHsv(c2 Color, t float64) Color { h1, s1, v1 := c1.Hsv() h2, s2, v2 := c2.Hsv() + // https://github.com/lucasb-eyer/go-colorful/pull/60 + if s1 == 0 && s2 != 0 { + h1 = h2 + } else if s2 == 0 && s1 != 0 { + h2 = h1 + } + // We know that h are both in [0..360] return Hsv(interp_angle(h1, h2, t), s1+t*(s2-s1), v1+t*(v2-v1)) } @@ -205,7 +239,7 @@ func (c1 Color) BlendHsv(c2 Color, t float64) Color { /// HSL /// /////////// -// Hsl returns the Hue [0..360], Saturation [0..1], and Luminance (lightness) [0..1] of the color. +// Hsl returns the Hue [0..359], Saturation [0..1], and Luminance (lightness) [0..1] of the color. func (col Color) Hsl() (h, s, l float64) { min := math.Min(math.Min(col.R, col.G), col.B) max := math.Max(math.Max(col.R, col.G), col.B) @@ -240,7 +274,7 @@ func (col Color) Hsl() (h, s, l float64) { return } -// Hsl creates a new Color given a Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1] +// Hsl creates a new Color given a Hue in [0..359], a Saturation [0..1], and a Luminance (lightness) in [0..1] func Hsl(h, s, l float64) Color { if s == 0 { return Color{l, l, l} @@ -331,23 +365,46 @@ func (col Color) Hex() string { // Hex parses a "html" hex color-string, either in the 3 "#f0c" or 6 "#ff1034" digits form. func Hex(scol string) (Color, error) { - format := "#%02x%02x%02x" - factor := 1.0 / 255.0 - if len(scol) == 4 { - format = "#%1x%1x%1x" - factor = 1.0 / 15.0 + if scol == "" || scol[0] != '#' { + return Color{}, fmt.Errorf("color: %v is not a hex-color", scol) + } + var c Color + var err error + switch len(scol) { + case 4: + c, err = parseHexColor(scol[1:2], scol[2:3], scol[3:4], 4, 1.0/15.0) + case 7: + c, err = parseHexColor(scol[1:3], scol[3:5], scol[5:7], 8, 1.0/255.0) + default: + return Color{}, fmt.Errorf("color: %v is not a hex-color", scol) } - - var r, g, b uint8 - n, err := fmt.Sscanf(scol, format, &r, &g, &b) if err != nil { + return Color{}, fmt.Errorf("color: %v is not a hex-color: %w", scol, err) + } + return c, nil +} + +func parseHexColor(r, g, b string, bits int, factor float64) (Color, error) { + var c Color + var v uint64 + var err error + + if v, err = strconv.ParseUint(r, 16, bits); err != nil { return Color{}, err } - if n != 3 { - return Color{}, fmt.Errorf("color: %v is not a hex-color", scol) + c.R = float64(v) * factor + + if v, err = strconv.ParseUint(g, 16, bits); err != nil { + return Color{}, err } + c.G = float64(v) * factor - return Color{float64(r) * factor, float64(g) * factor, float64(b) * factor}, nil + if v, err = strconv.ParseUint(b, 16, bits); err != nil { + return Color{}, err + } + c.B = float64(v) * factor + + return c, err } /// Linear /// @@ -377,7 +434,7 @@ func linearize_fast(v float64) float64 { v2 := v1 * v1 v3 := v2 * v1 v4 := v2 * v2 - //v5 := v3*v2 + // v5 := v3*v2 return -0.248750514614486 + 0.925583310193438*v + 1.16740237321695*v2 + 0.280457026598666*v3 - 0.0757991963780179*v4 //+ 0.0437040411548932*v5 } @@ -450,6 +507,19 @@ func LinearRgbToXyz(r, g, b float64) (x, y, z float64) { return } +// BlendLinearRgb blends two colors in the Linear RGB color-space. +// Unlike BlendRgb, this will not produce dark color around the center. +// t == 0 results in c1, t == 1 results in c2 +func (c1 Color) BlendLinearRgb(c2 Color, t float64) Color { + r1, g1, b1 := c1.LinearRgb() + r2, g2, b2 := c2.LinearRgb() + return LinearRgb( + r1+t*(r2-r1), + g1+t*(g2-g1), + b1+t*(b2-b1), + ) +} + /// XYZ /// /////////// // http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/ @@ -784,7 +854,7 @@ func LuvToXyz(l, u, v float64) (x, y, z float64) { } func LuvToXyzWhiteRef(l, u, v float64, wref [3]float64) (x, y, z float64) { - //y = wref[1] * lab_finv((l + 0.16) / 1.16) + // y = wref[1] * lab_finv((l + 0.16) / 1.16) if l <= 0.08 { y = wref[1] * l * 100.0 * 3.0 / 29.0 * 3.0 / 29.0 * 3.0 / 29.0 } else { @@ -913,6 +983,13 @@ func (col1 Color) BlendHcl(col2 Color, t float64) Color { h1, c1, l1 := col1.Hcl() h2, c2, l2 := col2.Hcl() + // https://github.com/lucasb-eyer/go-colorful/pull/60 + if c1 <= 0.00015 && c2 >= 0.00015 { + h1 = h2 + } else if c2 <= 0.00015 && c1 >= 0.00015 { + h2 = h1 + } + // We know that h are both in [0..360] return Hcl(interp_angle(h1, h2, t), c1+t*(c2-c1), l1+t*(l2-l1)).Clamped() } @@ -977,3 +1054,103 @@ func (col1 Color) BlendLuvLCh(col2 Color, t float64) Color { // We know that h are both in [0..360] return LuvLCh(l1+t*(l2-l1), c1+t*(c2-c1), interp_angle(h1, h2, t)) } + +/// OkLab /// +/////////// + +func (col Color) OkLab() (l, a, b float64) { + return XyzToOkLab(col.Xyz()) +} + +func OkLab(l, a, b float64) Color { + return Xyz(OkLabToXyz(l, a, b)) +} + +func XyzToOkLab(x, y, z float64) (l, a, b float64) { + l_ := math.Cbrt(0.8189330101*x + 0.3618667424*y - 0.1288597137*z) + m_ := math.Cbrt(0.0329845436*x + 0.9293118715*y + 0.0361456387*z) + s_ := math.Cbrt(0.0482003018*x + 0.2643662691*y + 0.6338517070*z) + l = 0.2104542553*l_ + 0.7936177850*m_ - 0.0040720468*s_ + a = 1.9779984951*l_ - 2.4285922050*m_ + 0.4505937099*s_ + b = 0.0259040371*l_ + 0.7827717662*m_ - 0.8086757660*s_ + return +} + +func OkLabToXyz(l, a, b float64) (x, y, z float64) { + l_ := 0.9999999984505196*l + 0.39633779217376774*a + 0.2158037580607588*b + m_ := 1.0000000088817607*l - 0.10556134232365633*a - 0.0638541747717059*b + s_ := 1.0000000546724108*l - 0.08948418209496574*a - 1.2914855378640917*b + + ll := math.Pow(l_, 3) + m := math.Pow(m_, 3) + s := math.Pow(s_, 3) + + x = 1.2268798733741557*ll - 0.5578149965554813*m + 0.28139105017721594*s + y = -0.04057576262431372*ll + 1.1122868293970594*m - 0.07171106666151696*s + z = -0.07637294974672142*ll - 0.4214933239627916*m + 1.5869240244272422*s + + return +} + +// BlendOkLab blends two colors in the OkLab color-space, which should result in a better blend (even compared to BlendLab). +func (c1 Color) BlendOkLab(c2 Color, t float64) Color { + l1, a1, b1 := c1.OkLab() + l2, a2, b2 := c2.OkLab() + return OkLab(l1+t*(l2-l1), + a1+t*(a2-a1), + b1+t*(b2-b1)) +} + +/// OkLch /// +/////////// + +func (col Color) OkLch() (l, c, h float64) { + return OkLabToOkLch(col.OkLab()) +} + +func OkLch(l, c, h float64) Color { + return Xyz(OkLchToXyz(l, c, h)) +} + +func XyzToOkLch(x, y, z float64) (float64, float64, float64) { + l, c, h := OkLabToOkLch(XyzToOkLab(x, y, z)) + return l, c, h +} + +func OkLchToXyz(l, c, h float64) (float64, float64, float64) { + x, y, z := OkLabToXyz(OkLchToOkLab(l, c, h)) + return x, y, z +} + +func OkLabToOkLch(l, a, b float64) (float64, float64, float64) { + c := math.Sqrt((a * a) + (b * b)) + h := math.Atan2(b, a) + if h < 0 { + h += 2 * math.Pi + } + + return l, c, h * 180 / math.Pi +} + +func OkLchToOkLab(l, c, h float64) (float64, float64, float64) { + h *= math.Pi / 180 + a := c * math.Cos(h) + b := c * math.Sin(h) + return l, a, b +} + +// BlendOkLch blends two colors in the OkLch color-space, which should result in a better blend (even compared to BlendHcl). +func (col1 Color) BlendOkLch(col2 Color, t float64) Color { + l1, c1, h1 := col1.OkLch() + l2, c2, h2 := col2.OkLch() + + // https://github.com/lucasb-eyer/go-colorful/pull/60 + if c1 <= 0.00015 && c2 >= 0.00015 { + h1 = h2 + } else if c2 <= 0.00015 && c1 >= 0.00015 { + h2 = h1 + } + + // We know that h are both in [0..360] + return OkLch(l1+t*(l2-l1), c1+t*(c2-c1), interp_angle(h1, h2, t)).Clamped() +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go b/vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go index bb66dfa4..0cb9286c 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go +++ b/vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go @@ -1,13 +1,9 @@ package colorful -import ( - "math/rand" -) - // Uses the HSV color space to generate colors with similar S,V but distributed // evenly along their Hue. This is fast but not always pretty. // If you've got time to spare, use Lab (the non-fast below). -func FastHappyPalette(colorsCount int) (colors []Color) { +func FastHappyPaletteWithRand(colorsCount int, rand RandInterface) (colors []Color) { colors = make([]Color, colorsCount) for i := 0; i < colorsCount; i++ { @@ -16,10 +12,18 @@ func FastHappyPalette(colorsCount int) (colors []Color) { return } -func HappyPalette(colorsCount int) ([]Color, error) { +func FastHappyPalette(colorsCount int) (colors []Color) { + return FastHappyPaletteWithRand(colorsCount, getDefaultGlobalRand()) +} + +func HappyPaletteWithRand(colorsCount int, rand RandInterface) ([]Color, error) { pimpy := func(l, a, b float64) bool { _, c, _ := LabToHcl(l, a, b) return 0.3 <= c && 0.4 <= l && l <= 0.8 } - return SoftPaletteEx(colorsCount, SoftPaletteSettings{pimpy, 50, true}) + return SoftPaletteExWithRand(colorsCount, SoftPaletteSettings{pimpy, 50, true}, rand) +} + +func HappyPalette(colorsCount int) ([]Color, error) { + return HappyPaletteWithRand(colorsCount, getDefaultGlobalRand()) } diff --git a/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go b/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go index 76f31d8f..26f35730 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go +++ b/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go @@ -34,6 +34,10 @@ func (hc *HexColor) Value() (driver.Value, error) { return Color(*hc).Hex(), nil } +func (hc HexColor) String() string { + return Color(hc).Hex() +} + func (e errUnsupportedType) Error() string { return fmt.Sprintf("unsupported type: got %v, want a %s", e.got, e.want) } @@ -65,3 +69,23 @@ func (hc *HexColor) Decode(hexCode string) error { *hc = HexColor(col) return nil } + +func (hc HexColor) MarshalYAML() (interface{}, error) { + return Color(hc).Hex(), nil +} + +func (hc *HexColor) UnmarshalYAML(unmarshal func(interface{}) error) error { + var hexCode string + if err := unmarshal(&hexCode); err != nil { + return err + } + + var col, err = Hex(hexCode) + if err != nil { + return err + } + + *hc = HexColor(col) + + return nil +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/hsluv.go b/vendor/github.com/lucasb-eyer/go-colorful/hsluv.go index d19fb644..cc514882 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/hsluv.go +++ b/vendor/github.com/lucasb-eyer/go-colorful/hsluv.go @@ -11,7 +11,8 @@ import "math" // comparing to the test values, this modified white reference is used internally. // // See this GitHub thread for details on these values: -// https://github.com/hsluv/hsluv/issues/79 +// +// https://github.com/hsluv/hsluv/issues/79 var hSLuvD65 = [3]float64{0.95045592705167, 1.0, 1.089057750759878} func LuvLChToHSLuv(l, c, h float64) (float64, float64, float64) { @@ -115,7 +116,7 @@ func (col Color) HPLuv() (h, s, l float64) { return LuvLChToHPLuv(col.LuvLChWhiteRef(hSLuvD65)) } -// DistanceHSLuv calculates Euclidan distance in the HSLuv colorspace. No idea +// DistanceHSLuv calculates Euclidean distance in the HSLuv colorspace. No idea // how useful this is. // // The Hue value is divided by 100 before the calculation, so that H, S, and L diff --git a/vendor/github.com/lucasb-eyer/go-colorful/rand.go b/vendor/github.com/lucasb-eyer/go-colorful/rand.go new file mode 100644 index 00000000..d3a2d5b5 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/rand.go @@ -0,0 +1,22 @@ +package colorful + +import "math/rand" + +type RandInterface interface { + Float64() float64 + Intn(n int) int +} + +type defaultGlobalRand struct{} + +func (df defaultGlobalRand) Float64() float64 { + return rand.Float64() +} + +func (df defaultGlobalRand) Intn(n int) int { + return rand.Intn(n) +} + +func getDefaultGlobalRand() RandInterface { + return defaultGlobalRand{} +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go b/vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go index 9f7bf6f7..6d8aa137 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go +++ b/vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go @@ -6,7 +6,6 @@ package colorful import ( "fmt" "math" - "math/rand" ) // The algorithm works in L*a*b* color space and converts to RGB in the end. @@ -32,7 +31,7 @@ type SoftPaletteSettings struct { // as a new palette of distinctive colors. Falls back to K-medoid if the mean // happens to fall outside of the color-space, which can only happen if you // specify a CheckColor function. -func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, error) { +func SoftPaletteExWithRand(colorsCount int, settings SoftPaletteSettings, rand RandInterface) ([]Color, error) { // Checks whether it's a valid RGB and also fulfills the potentially provided constraint. check := func(col lab_t) bool { @@ -79,7 +78,7 @@ func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, erro // The actual k-means/medoid iterations for i := 0; i < settings.Iterations; i++ { - // Reassing the samples to clusters, i.e. to their closest mean. + // Reassigning the samples to clusters, i.e. to their closest mean. // By the way, also check if any sample is used as a medoid and if so, mark that. for isample, sample := range samples { samples_used[isample] = false @@ -100,7 +99,7 @@ func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, erro // Compute new means according to the samples. for imean := range means { - // The new mean is the average of all samples belonging to it.. + // The new mean is the average of all samples belonging to it. nsamples := 0 newmean := lab_t{0.0, 0.0, 0.0} for isample, sample := range samples { @@ -148,9 +147,17 @@ func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, erro return labs2cols(means), nil } +func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, error) { + return SoftPaletteExWithRand(colorsCount, settings, getDefaultGlobalRand()) +} + // A wrapper which uses common parameters. +func SoftPaletteWithRand(colorsCount int, rand RandInterface) ([]Color, error) { + return SoftPaletteExWithRand(colorsCount, SoftPaletteSettings{nil, 50, false}, rand) +} + func SoftPalette(colorsCount int) ([]Color, error) { - return SoftPaletteEx(colorsCount, SoftPaletteSettings{nil, 50, false}) + return SoftPaletteWithRand(colorsCount, getDefaultGlobalRand()) } func in(haystack []lab_t, upto int, needle lab_t) bool { diff --git a/vendor/github.com/lucasb-eyer/go-colorful/sort.go b/vendor/github.com/lucasb-eyer/go-colorful/sort.go new file mode 100644 index 00000000..b1c1b681 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/sort.go @@ -0,0 +1,191 @@ +// This file provides functions for sorting colors. + +package colorful + +import ( + "math" + "sort" +) + +// An element represents a single element of a set. It is used to +// implement a disjoint-set forest. +type element struct { + parent *element // Parent element + rank int // Rank (approximate depth) of the subtree with this element as root +} + +// newElement creates a singleton set and returns its sole element. +func newElement() *element { + s := &element{} + s.parent = s + return s +} + +// find returns an arbitrary element of a set when invoked on any element of +// the set, The important feature is that it returns the same value when +// invoked on any element of the set. Consequently, it can be used to test if +// two elements belong to the same set. +func (e *element) find() *element { + for e.parent != e { + e.parent = e.parent.parent + e = e.parent + } + return e +} + +// union establishes the union of two sets when given an element from each set. +// Afterwards, the original sets no longer exist as separate entities. +func union(e1, e2 *element) { + // Ensure the two elements aren't already part of the same union. + e1Root := e1.find() + e2Root := e2.find() + if e1Root == e2Root { + return + } + + // Create a union by making the shorter tree point to the root of the + // larger tree. + switch { + case e1Root.rank < e2Root.rank: + e1Root.parent = e2Root + case e1Root.rank > e2Root.rank: + e2Root.parent = e1Root + default: + e2Root.parent = e1Root + e1Root.rank++ + } +} + +// An edgeIdxs describes an edge in a graph or tree. The vertices in the edge +// are indexes into a list of Color values. +type edgeIdxs [2]int + +// An edgeDistance is a map from an edge (pair of indices) to a distance +// between the two vertices. +type edgeDistance map[edgeIdxs]float64 + +// allToAllDistancesCIEDE2000 computes the CIEDE2000 distance between each pair of +// colors. It returns a map from a pair of indices (u, v) with u < v to a +// distance. +func allToAllDistancesCIEDE2000(cs []Color) edgeDistance { + nc := len(cs) + m := make(edgeDistance, nc*nc) + for u := 0; u < nc-1; u++ { + for v := u + 1; v < nc; v++ { + m[edgeIdxs{u, v}] = cs[u].DistanceCIEDE2000(cs[v]) + } + } + return m +} + +// sortEdges sorts all edges in a distance map by increasing vertex distance. +func sortEdges(m edgeDistance) []edgeIdxs { + es := make([]edgeIdxs, 0, len(m)) + for uv := range m { + es = append(es, uv) + } + sort.Slice(es, func(i, j int) bool { + return m[es[i]] < m[es[j]] + }) + return es +} + +// minSpanTree computes a minimum spanning tree from a vertex count and a +// distance-sorted edge list. It returns the subset of edges that belong to +// the tree, including both (u, v) and (v, u) for each edge. +func minSpanTree(nc int, es []edgeIdxs) map[edgeIdxs]struct{} { + // Start with each vertex in its own set. + elts := make([]*element, nc) + for i := range elts { + elts[i] = newElement() + } + + // Run Kruskal's algorithm to construct a minimal spanning tree. + mst := make(map[edgeIdxs]struct{}, nc) + for _, uv := range es { + u, v := uv[0], uv[1] + if elts[u].find() == elts[v].find() { + continue // Same set: edge would introduce a cycle. + } + mst[uv] = struct{}{} + mst[edgeIdxs{v, u}] = struct{}{} + union(elts[u], elts[v]) + } + return mst +} + +// traverseMST walks a minimum spanning tree in prefix order. +func traverseMST(mst map[edgeIdxs]struct{}, root int) []int { + // Compute a list of neighbors for each vertex. + neighs := make(map[int][]int, len(mst)) + for uv := range mst { + u, v := uv[0], uv[1] + neighs[u] = append(neighs[u], v) + } + for u, vs := range neighs { + sort.Ints(vs) + copy(neighs[u], vs) + } + + // Walk the tree from a given vertex. + order := make([]int, 0, len(neighs)) + visited := make(map[int]bool, len(neighs)) + var walkFrom func(int) + walkFrom = func(r int) { + // Visit the starting vertex. + order = append(order, r) + visited[r] = true + + // Recursively visit each child in turn. + for _, c := range neighs[r] { + if !visited[c] { + walkFrom(c) + } + } + } + walkFrom(root) + return order +} + +// Sorted sorts a list of Color values. Sorting is not a well-defined operation +// for colors so the intention here primarily is to order colors so that the +// transition from one to the next is fairly smooth. +func Sorted(cs []Color) []Color { + // Do nothing in trivial cases. + newCs := make([]Color, len(cs)) + if len(cs) < 2 { + copy(newCs, cs) + return newCs + } + + // Compute the distance from each color to every other color. + dists := allToAllDistancesCIEDE2000(cs) + + // Produce a list of edges in increasing order of the distance between + // their vertices. + edges := sortEdges(dists) + + // Construct a minimum spanning tree from the list of edges. + mst := minSpanTree(len(cs), edges) + + // Find the darkest color in the list. + var black Color + var dIdx int // Index of darkest color + light := math.MaxFloat64 // Lightness of darkest color (distance from black) + for i, c := range cs { + d := black.DistanceCIEDE2000(c) + if d < light { + dIdx = i + light = d + } + } + + // Traverse the tree starting from the darkest color. + idxs := traverseMST(mst, dIdx) + + // Convert the index list to a list of colors, overwriting the input. + for i, idx := range idxs { + newCs[i] = cs[idx] + } + return newCs +} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go b/vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go index 00f42a5c..d294fb41 100644 --- a/vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go +++ b/vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go @@ -1,13 +1,9 @@ package colorful -import ( - "math/rand" -) - // Uses the HSV color space to generate colors with similar S,V but distributed // evenly along their Hue. This is fast but not always pretty. // If you've got time to spare, use Lab (the non-fast below). -func FastWarmPalette(colorsCount int) (colors []Color) { +func FastWarmPaletteWithRand(colorsCount int, rand RandInterface) (colors []Color) { colors = make([]Color, colorsCount) for i := 0; i < colorsCount; i++ { @@ -16,10 +12,18 @@ func FastWarmPalette(colorsCount int) (colors []Color) { return } -func WarmPalette(colorsCount int) ([]Color, error) { +func FastWarmPalette(colorsCount int) (colors []Color) { + return FastWarmPaletteWithRand(colorsCount, getDefaultGlobalRand()) +} + +func WarmPaletteWithRand(colorsCount int, rand RandInterface) ([]Color, error) { warmy := func(l, a, b float64) bool { _, c, _ := LabToHcl(l, a, b) return 0.1 <= c && c <= 0.4 && 0.2 <= l && l <= 0.5 } - return SoftPaletteEx(colorsCount, SoftPaletteSettings{warmy, 50, true}) + return SoftPaletteExWithRand(colorsCount, SoftPaletteSettings{warmy, 50, true}, rand) +} + +func WarmPalette(colorsCount int) ([]Color, error) { + return WarmPaletteWithRand(colorsCount, getDefaultGlobalRand()) } diff --git a/vendor/github.com/lucasb-eyer/go-colorful/widegamut.go b/vendor/github.com/lucasb-eyer/go-colorful/widegamut.go new file mode 100644 index 00000000..63c3e878 --- /dev/null +++ b/vendor/github.com/lucasb-eyer/go-colorful/widegamut.go @@ -0,0 +1,290 @@ +package colorful + +import "math" + +// Wide-gamut RGB color spaces from CSS Color Level 4. +// https://www.w3.org/TR/css-color-4/#color-conversion-code + +/// Bradford /// +//////////////// +// Bradford chromatic adaptation between D50 and D65 illuminants. + +func D50ToD65(x, y, z float64) (xo, yo, zo float64) { + xo = 0.9554734527042182*x - 0.023098536874261423*y + 0.06325964552894382*z + yo = -0.028369706963208136*x + 1.0099954580058226*y + 0.021041398966943008*z + zo = 0.012314001688319899*x - 0.020507696433477912*y + 1.3303659366080753*z + return +} + +func D65ToD50(x, y, z float64) (xo, yo, zo float64) { + xo = 1.0479298208405488*x + 0.022946793341019088*y - 0.05019222954313557*z + yo = 0.029627815688159344*x + 0.990434484573249*y - 0.01707382502938514*z + zo = -0.009243058152591178*x + 0.015055144896577895*y + 0.7518742899580008*z + return +} + +/// XYZ D50 /// +/////////////// + +func XyzD50(x, y, z float64) Color { + return Xyz(D50ToD65(x, y, z)) +} + +func (col Color) XyzD50() (x, y, z float64) { + return D65ToD50(col.Xyz()) +} + +/// Display P3 /// +////////////////// +// Uses the sRGB transfer function with DCI-P3 primaries. + +func DisplayP3ToLinearRgb(r, g, b float64) (rl, gl, bl float64) { + rl = linearize(r) + gl = linearize(g) + bl = linearize(b) + return +} + +func LinearDisplayP3ToXyz(r, g, b float64) (x, y, z float64) { + x = 0.4865709486482162*r + 0.26566769316909306*g + 0.1982172852343625*b + y = 0.2289745640697488*r + 0.6917385218365064*g + 0.079286914093745*b + z = 0.04511338185890264*g + 1.043944368900976*b + return +} + +func XyzToLinearDisplayP3(x, y, z float64) (r, g, b float64) { + r = 2.493496911941425*x - 0.9313836179191239*y - 0.40271078445071684*z + g = -0.8294889695615747*x + 1.7626640603183463*y + 0.023624685841943577*z + b = 0.035845830243784335*x - 0.07617238926804182*y + 0.9568845240076872*z + return +} + +func DisplayP3(r, g, b float64) Color { + rl, gl, bl := DisplayP3ToLinearRgb(r, g, b) + x, y, z := LinearDisplayP3ToXyz(rl, gl, bl) + return Xyz(x, y, z) +} + +func (col Color) DisplayP3() (r, g, b float64) { + x, y, z := col.Xyz() + rl, gl, bl := XyzToLinearDisplayP3(x, y, z) + r = delinearize(rl) + g = delinearize(gl) + b = delinearize(bl) + return +} + +// BlendDisplayP3 blends two colors in the Display P3 color-space. +// t == 0 results in c1, t == 1 results in c2 +func (c1 Color) BlendDisplayP3(c2 Color, t float64) Color { + r1, g1, b1 := c1.DisplayP3() + r2, g2, b2 := c2.DisplayP3() + return DisplayP3( + r1+t*(r2-r1), + g1+t*(g2-g1), + b1+t*(b2-b1)) +} + +/// A98 RGB /// +/////////////// +// Adobe RGB (1998) color space. + +func linearizeA98(v float64) float64 { + sign := 1.0 + if v < 0 { + sign = -1.0 + v = -v + } + return sign * math.Pow(v, 563.0/256.0) +} + +func delinearizeA98(v float64) float64 { + sign := 1.0 + if v < 0 { + sign = -1.0 + v = -v + } + return sign * math.Pow(v, 256.0/563.0) +} + +func A98RgbToLinearRgb(r, g, b float64) (rl, gl, bl float64) { + rl = linearizeA98(r) + gl = linearizeA98(g) + bl = linearizeA98(b) + return +} + +func LinearA98RgbToXyz(r, g, b float64) (x, y, z float64) { + x = 0.5766690429101305*r + 0.1855582379065463*g + 0.1882286462349947*b + y = 0.29734497525053605*r + 0.6273635662554661*g + 0.07529145849399788*b + z = 0.02703136138641234*r + 0.07068885253582723*g + 0.9913375368376388*b + return +} + +func XyzToLinearA98Rgb(x, y, z float64) (r, g, b float64) { + r = 2.0415879038107327*x - 0.5650069742788597*y - 0.34473135077832956*z + g = -0.9692436362808795*x + 1.8759675015077202*y + 0.04155505740717559*z + b = 0.013444280632031142*x - 0.11836239223101838*y + 1.0151749943912054*z + return +} + +func A98Rgb(r, g, b float64) Color { + rl, gl, bl := A98RgbToLinearRgb(r, g, b) + x, y, z := LinearA98RgbToXyz(rl, gl, bl) + return Xyz(x, y, z) +} + +func (col Color) A98Rgb() (r, g, b float64) { + x, y, z := col.Xyz() + rl, gl, bl := XyzToLinearA98Rgb(x, y, z) + r = delinearizeA98(rl) + g = delinearizeA98(gl) + b = delinearizeA98(bl) + return +} + +// BlendA98Rgb blends two colors in the A98 RGB color-space. +// t == 0 results in c1, t == 1 results in c2 +func (c1 Color) BlendA98Rgb(c2 Color, t float64) Color { + r1, g1, b1 := c1.A98Rgb() + r2, g2, b2 := c2.A98Rgb() + return A98Rgb( + r1+t*(r2-r1), + g1+t*(g2-g1), + b1+t*(b2-b1)) +} + +/// ProPhoto RGB /// +//////////////////// +// ProPhoto RGB (ROMM RGB) uses D50 illuminant. + +func linearizeProPhoto(v float64) float64 { + if v <= 16.0/512.0 { + return v / 16.0 + } + return math.Pow(v, 1.8) +} + +func delinearizeProPhoto(v float64) float64 { + if v < 1.0/512.0 { + return 16.0 * v + } + return math.Pow(v, 1.0/1.8) +} + +func ProPhotoRgbToLinearRgb(r, g, b float64) (rl, gl, bl float64) { + rl = linearizeProPhoto(r) + gl = linearizeProPhoto(g) + bl = linearizeProPhoto(b) + return +} + +func LinearProPhotoRgbToXyzD50(r, g, b float64) (x, y, z float64) { + x = 0.7977604896723027*r + 0.13518583717574031*g + 0.0313493495815248*b + y = 0.2880711282292934*r + 0.7118432178101014*g + 0.00008565396060525902*b + z = 0.8251046025104602 * b + return +} + +func XyzD50ToLinearProPhotoRgb(x, y, z float64) (r, g, b float64) { + r = 1.3457989731028281*x - 0.25558010007997534*y - 0.05110628506753401*z + g = -0.5446224939028347*x + 1.5082327413132781*y + 0.02053603239147973*z + b = 1.2119675456389454 * z + return +} + +func ProPhotoRgb(r, g, b float64) Color { + rl, gl, bl := ProPhotoRgbToLinearRgb(r, g, b) + x, y, z := LinearProPhotoRgbToXyzD50(rl, gl, bl) + return XyzD50(x, y, z) +} + +func (col Color) ProPhotoRgb() (r, g, b float64) { + x, y, z := col.XyzD50() + rl, gl, bl := XyzD50ToLinearProPhotoRgb(x, y, z) + r = delinearizeProPhoto(rl) + g = delinearizeProPhoto(gl) + b = delinearizeProPhoto(bl) + return +} + +// BlendProPhotoRgb blends two colors in the ProPhoto RGB color-space. +// t == 0 results in c1, t == 1 results in c2 +func (c1 Color) BlendProPhotoRgb(c2 Color, t float64) Color { + r1, g1, b1 := c1.ProPhotoRgb() + r2, g2, b2 := c2.ProPhotoRgb() + return ProPhotoRgb( + r1+t*(r2-r1), + g1+t*(g2-g1), + b1+t*(b2-b1)) +} + +/// Rec. 2020 /// +///////////////// +// ITU-R BT.2020 color space. + +const ( + rec2020Alpha = 1.09929682680944 + rec2020Beta = 0.018053968510807 +) + +func linearizeRec2020(v float64) float64 { + if v < rec2020Beta*4.5 { + return v / 4.5 + } + return math.Pow((v+rec2020Alpha-1)/rec2020Alpha, 1.0/0.45) +} + +func delinearizeRec2020(v float64) float64 { + if v < rec2020Beta { + return 4.5 * v + } + return rec2020Alpha*math.Pow(v, 0.45) - (rec2020Alpha - 1) +} + +func Rec2020ToLinearRgb(r, g, b float64) (rl, gl, bl float64) { + rl = linearizeRec2020(r) + gl = linearizeRec2020(g) + bl = linearizeRec2020(b) + return +} + +func LinearRec2020ToXyz(r, g, b float64) (x, y, z float64) { + x = 0.6369580483012914*r + 0.14461690358620832*g + 0.1688809751641721*b + y = 0.2627002120112671*r + 0.6779980715188708*g + 0.05930171646986196*b + z = 0.028072693049087428*g + 1.0609850577107909*b + return +} + +func XyzToLinearRec2020(x, y, z float64) (r, g, b float64) { + r = 1.7166511879712674*x - 0.35567078377639233*y - 0.25336628137365974*z + g = -0.666684351832489*x + 1.616481236634939*y + 0.0157685458139402*z + b = 0.017639857445310783*x - 0.042770613257808524*y + 0.9421031212354738*z + return +} + +func Rec2020(r, g, b float64) Color { + rl, gl, bl := Rec2020ToLinearRgb(r, g, b) + x, y, z := LinearRec2020ToXyz(rl, gl, bl) + return Xyz(x, y, z) +} + +func (col Color) Rec2020() (r, g, b float64) { + x, y, z := col.Xyz() + rl, gl, bl := XyzToLinearRec2020(x, y, z) + r = delinearizeRec2020(rl) + g = delinearizeRec2020(gl) + b = delinearizeRec2020(bl) + return +} + +// BlendRec2020 blends two colors in the Rec. 2020 color-space. +// t == 0 results in c1, t == 1 results in c2 +func (c1 Color) BlendRec2020(c2 Color, t float64) Color { + r1, g1, b1 := c1.Rec2020() + r2, g2, b2 := c2.Rec2020() + return Rec2020( + r1+t*(r2-r1), + g1+t*(g2-g1), + b1+t*(b2-b1)) +} diff --git a/vendor/github.com/manuelarte/funcorder/analyzer/analyzer.go b/vendor/github.com/manuelarte/funcorder/analyzer/analyzer.go index c3112107..1e439175 100644 --- a/vendor/github.com/manuelarte/funcorder/analyzer/analyzer.go +++ b/vendor/github.com/manuelarte/funcorder/analyzer/analyzer.go @@ -14,6 +14,7 @@ const ( ConstructorCheckName = "constructor" StructMethodCheckName = "struct-method" AlphabeticalCheckName = "alphabetical" + FunctionCheckName = "function" ) func NewAnalyzer() *analysis.Analyzer { @@ -33,6 +34,8 @@ func NewAnalyzer() *analysis.Analyzer { "Checks if the exported methods of a structure are placed before the unexported ones.") a.Flags.BoolVar(&f.alphabeticalCheck, AlphabeticalCheckName, false, "Checks if the constructors and/or structure methods are sorted alphabetically.") + a.Flags.BoolVar(&f.functionCheck, FunctionCheckName, false, + "Checks that exported functions are placed before unexported functions.") return a } @@ -41,9 +44,16 @@ type funcorder struct { constructorCheck bool structMethodCheck bool alphabeticalCheck bool + functionCheck bool } func (f *funcorder) run(pass *analysis.Pass) (any, error) { + insp, found := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + if !found { + //nolint:nilnil // impossible case. + return nil, nil + } + var enabledCheckers internal.Feature if f.constructorCheck { enabledCheckers.Enable(internal.ConstructorCheck) @@ -57,14 +67,12 @@ func (f *funcorder) run(pass *analysis.Pass) (any, error) { enabledCheckers.Enable(internal.AlphabeticalCheck) } - fp := internal.NewFileProcessor(pass.Fset, enabledCheckers) - - insp, found := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - if !found { - //nolint:nilnil // impossible case. - return nil, nil + if f.functionCheck { + enabledCheckers.Enable(internal.FunctionCheck) } + fp := internal.NewFileProcessor(enabledCheckers) + nodeFilter := []ast.Node{ (*ast.File)(nil), (*ast.FuncDecl)(nil), @@ -74,23 +82,18 @@ func (f *funcorder) run(pass *analysis.Pass) (any, error) { insp.Preorder(nodeFilter, func(n ast.Node) { switch node := n.(type) { case *ast.File: - for _, report := range fp.Analyze() { - pass.Report(report) - } - - fp.NewFileNode(node) + fp.Analyze(pass) + fp.ResetStructs() case *ast.FuncDecl: - fp.NewFuncDecl(node) + fp.AddFuncDecl(node) case *ast.TypeSpec: - fp.NewTypeSpec(node) + fp.AddTypeSpec(node) } }) - for _, report := range fp.Analyze() { - pass.Report(report) - } + fp.Analyze(pass) //nolint:nilnil //any, error return nil, nil diff --git a/vendor/github.com/manuelarte/funcorder/internal/astutils.go b/vendor/github.com/manuelarte/funcorder/internal/astutils.go deleted file mode 100644 index af7fa8c8..00000000 --- a/vendor/github.com/manuelarte/funcorder/internal/astutils.go +++ /dev/null @@ -1,93 +0,0 @@ -package internal - -import ( - "bytes" - "go/ast" - "go/format" - "go/token" - "strings" -) - -func FuncCanBeConstructor(n *ast.FuncDecl) bool { - if !n.Name.IsExported() || n.Recv != nil { - return false - } - - if n.Type.Results == nil || len(n.Type.Results.List) == 0 { - return false - } - - for _, prefix := range []string{"new", "must"} { - if strings.HasPrefix(strings.ToLower(n.Name.Name), prefix) && - len(n.Name.Name) > len(prefix) { // TODO(ldez): bug if the name is just `New`. - return true - } - } - - return false -} - -func FuncIsMethod(n *ast.FuncDecl) (*ast.Ident, bool) { - if n.Recv == nil { - return nil, false - } - - if len(n.Recv.List) != 1 { - return nil, false - } - - if recv, ok := GetIdent(n.Recv.List[0].Type); ok { - return recv, true - } - - return nil, false -} - -func GetIdent(expr ast.Expr) (*ast.Ident, bool) { - switch exp := expr.(type) { - case *ast.StarExpr: - return GetIdent(exp.X) - - case *ast.Ident: - return exp, true - - default: - return nil, false - } -} - -// GetStartingPos returns the token starting position of the function -// taking into account if there are comments. -func GetStartingPos(function *ast.FuncDecl) token.Pos { - startingPos := function.Pos() - if function.Doc != nil { - startingPos = function.Doc.Pos() - } - - return startingPos -} - -// NodeToBytes convert the ast.Node in bytes. -func NodeToBytes(fset *token.FileSet, node ast.Node) ([]byte, error) { - var buf bytes.Buffer - if err := format.Node(&buf, fset, node); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -// SplitExportedUnexported split functions/methods based on whether they are exported or not. -// -//nolint:nonamedreturns // names serve as documentation -func SplitExportedUnexported(funcDecls []*ast.FuncDecl) (exported, unexported []*ast.FuncDecl) { - for _, f := range funcDecls { - if f.Name.IsExported() { - exported = append(exported, f) - } else { - unexported = append(unexported, f) - } - } - - return exported, unexported -} diff --git a/vendor/github.com/manuelarte/funcorder/internal/features.go b/vendor/github.com/manuelarte/funcorder/internal/features.go index 55d5caba..7012a688 100644 --- a/vendor/github.com/manuelarte/funcorder/internal/features.go +++ b/vendor/github.com/manuelarte/funcorder/internal/features.go @@ -4,6 +4,7 @@ const ( ConstructorCheck Feature = 1 << iota StructMethodCheck AlphabeticalCheck + FunctionCheck ) type Feature uint8 diff --git a/vendor/github.com/manuelarte/funcorder/internal/file_processor.go b/vendor/github.com/manuelarte/funcorder/internal/file_processor.go index 88ae00f2..9276c4a0 100644 --- a/vendor/github.com/manuelarte/funcorder/internal/file_processor.go +++ b/vendor/github.com/manuelarte/funcorder/internal/file_processor.go @@ -2,69 +2,102 @@ package internal import ( "go/ast" - "go/token" "golang.org/x/tools/go/analysis" ) // FileProcessor Holder to store all the functions that are potential to be constructors and all the structs. type FileProcessor struct { - fset *token.FileSet - structs map[string]*StructHolder - features Feature + structs map[string]*StructHolder + features Feature + topLevelFuncs []*ast.FuncDecl } // NewFileProcessor creates a new file processor. -func NewFileProcessor(fset *token.FileSet, checkers Feature) *FileProcessor { +func NewFileProcessor(checkers Feature) *FileProcessor { return &FileProcessor{ - fset: fset, structs: make(map[string]*StructHolder), features: checkers, } } // Analyze check whether the order of the methods in the constructor is correct. -func (fp *FileProcessor) Analyze() []analysis.Diagnostic { - var reports []analysis.Diagnostic - +func (fp *FileProcessor) Analyze(pass *analysis.Pass) { for _, sh := range fp.structs { // filter out structs that are not declared inside that file if sh.Struct != nil { - reports = append(reports, sh.Analyze()...) + sh.Analyze(pass) } } - return reports + if fp.features.IsEnabled(FunctionCheck) { + fp.analyzeFunctions(pass) + } } -func (fp *FileProcessor) NewFileNode(_ *ast.File) { +func (fp *FileProcessor) ResetStructs() { fp.structs = make(map[string]*StructHolder) + fp.topLevelFuncs = nil } -func (fp *FileProcessor) NewFuncDecl(n *ast.FuncDecl) { - if sc, ok := NewStructConstructor(n); ok { - fp.addConstructor(sc) +func (fp *FileProcessor) AddFuncDecl(n *ast.FuncDecl) { + if fp.features.IsEnabled(FunctionCheck) && n.Recv == nil { + fp.topLevelFuncs = append(fp.topLevelFuncs, n) + } + + if sc := NewStructConstructor(n); sc != nil { + sh := fp.getOrCreate(sc.StructReturn.Name) + sh.Constructors = append(sh.Constructors, sc.Constructor) + return } - if st, ok := FuncIsMethod(n); ok { - fp.addMethod(st.Name, n) + if st := funcIsMethod(n); st != nil { + sh := fp.getOrCreate(st.Name) + sh.StructMethods = append(sh.StructMethods, n) } } -func (fp *FileProcessor) NewTypeSpec(n *ast.TypeSpec) { +func (fp *FileProcessor) AddTypeSpec(n *ast.TypeSpec) { sh := fp.getOrCreate(n.Name.Name) sh.Struct = n } -func (fp *FileProcessor) addConstructor(sc StructConstructor) { - sh := fp.getOrCreate(sc.GetStructReturn().Name) - sh.AddConstructor(sc.GetConstructor()) -} +// analyzeFunctions reports every unexported top-level function that appears +// before the last exported top-level function in source order. +// The `init` function is excluded from this check. +func (fp *FileProcessor) analyzeFunctions(pass *analysis.Pass) { + var lastExported *ast.FuncDecl + + for _, fn := range fp.topLevelFuncs { + if fn.Name.Name == "init" { + continue + } + + if !fn.Name.IsExported() { + continue + } + + if lastExported == nil || fn.Pos() > lastExported.Pos() { + lastExported = fn + } + } + + if lastExported == nil { + return + } -func (fp *FileProcessor) addMethod(st string, n *ast.FuncDecl) { - sh := fp.getOrCreate(st) - sh.AddMethod(n) + for _, fn := range fp.topLevelFuncs { + if fn.Name.Name == "init" { + continue + } + + if fn.Name.IsExported() || fn.Pos() >= lastExported.Pos() { + continue + } + + reportUnexportedFuncBeforeExportedFunc(pass, fn, lastExported) + } } func (fp *FileProcessor) getOrCreate(structName string) *StructHolder { @@ -73,10 +106,34 @@ func (fp *FileProcessor) getOrCreate(structName string) *StructHolder { } created := &StructHolder{ - Fset: fp.fset, Features: fp.features, } fp.structs[structName] = created return created } + +func funcIsMethod(n *ast.FuncDecl) *ast.Ident { + if n.Recv == nil { + return nil + } + + if len(n.Recv.List) != 1 { + return nil + } + + return getIdent(n.Recv.List[0].Type) +} + +func getIdent(expr ast.Expr) *ast.Ident { + switch exp := expr.(type) { + case *ast.StarExpr: + return getIdent(exp.X) + + case *ast.Ident: + return exp + + default: + return nil + } +} diff --git a/vendor/github.com/manuelarte/funcorder/internal/diag.go b/vendor/github.com/manuelarte/funcorder/internal/reports.go similarity index 58% rename from vendor/github.com/manuelarte/funcorder/internal/diag.go rename to vendor/github.com/manuelarte/funcorder/internal/reports.go index faf2ffdd..350ac05e 100644 --- a/vendor/github.com/manuelarte/funcorder/internal/diag.go +++ b/vendor/github.com/manuelarte/funcorder/internal/reports.go @@ -7,63 +7,72 @@ import ( "golang.org/x/tools/go/analysis" ) -func NewConstructorNotAfterStructType(structSpec *ast.TypeSpec, constructor *ast.FuncDecl) analysis.Diagnostic { - return analysis.Diagnostic{ +func reportConstructorNotAfterStructType(pass *analysis.Pass, structSpec *ast.TypeSpec, constructor *ast.FuncDecl) { + pass.Report(analysis.Diagnostic{ Pos: constructor.Pos(), Message: fmt.Sprintf("constructor %q for struct %q should be placed after the struct declaration", constructor.Name, structSpec.Name), URL: "https://github.com/manuelarte/funcorder?tab=readme-ov-file#check-constructors-functions-are-placed-after-struct-declaration", //nolint:lll // url - } + }) } -func NewConstructorNotBeforeStructMethod( +func reportConstructorNotBeforeStructMethod( + pass *analysis.Pass, structSpec *ast.TypeSpec, - constructor *ast.FuncDecl, - method *ast.FuncDecl, -) analysis.Diagnostic { - return analysis.Diagnostic{ + constructor, method *ast.FuncDecl, +) { + pass.Report(analysis.Diagnostic{ Pos: constructor.Pos(), URL: "https://github.com/manuelarte/funcorder?tab=readme-ov-file#check-constructors-functions-are-placed-after-struct-declaration", //nolint:lll // url Message: fmt.Sprintf("constructor %q for struct %q should be placed before struct method %q", constructor.Name, structSpec.Name, method.Name), - } + }) } -func NewAdjacentConstructorsNotSortedAlphabetically( +func reportAdjacentConstructorsNotSortedAlphabetically( + pass *analysis.Pass, structSpec *ast.TypeSpec, - constructorNotSorted *ast.FuncDecl, - otherConstructorNotSorted *ast.FuncDecl, -) analysis.Diagnostic { - return analysis.Diagnostic{ + constructorNotSorted, otherConstructorNotSorted *ast.FuncDecl, +) { + pass.Report(analysis.Diagnostic{ Pos: otherConstructorNotSorted.Pos(), URL: "https://github.com/manuelarte/funcorder?tab=readme-ov-file#check-constructorsmethods-are-sorted-alphabetically", Message: fmt.Sprintf("constructor %q for struct %q should be placed before constructor %q", otherConstructorNotSorted.Name, structSpec.Name, constructorNotSorted.Name), - } + }) } -func NewUnexportedMethodBeforeExportedForStruct( +func reportUnexportedMethodBeforeExportedForStruct( + pass *analysis.Pass, structSpec *ast.TypeSpec, - privateMethod *ast.FuncDecl, - publicMethod *ast.FuncDecl, -) analysis.Diagnostic { - return analysis.Diagnostic{ + privateMethod, publicMethod *ast.FuncDecl, +) { + pass.Report(analysis.Diagnostic{ Pos: privateMethod.Pos(), URL: "https://github.com/manuelarte/funcorder?tab=readme-ov-file#check-exported-methods-are-placed-before-unexported-methods", //nolint:lll // url Message: fmt.Sprintf("unexported method %q for struct %q should be placed after the exported method %q", privateMethod.Name, structSpec.Name, publicMethod.Name), - } + }) } -func NewAdjacentStructMethodsNotSortedAlphabetically( +func reportAdjacentStructMethodsNotSortedAlphabetically( + pass *analysis.Pass, structSpec *ast.TypeSpec, - method *ast.FuncDecl, - otherMethod *ast.FuncDecl, -) analysis.Diagnostic { - return analysis.Diagnostic{ + method, otherMethod *ast.FuncDecl, +) { + pass.Report(analysis.Diagnostic{ Pos: otherMethod.Pos(), URL: "https://github.com/manuelarte/funcorder?tab=readme-ov-file#check-constructorsmethods-are-sorted-alphabetically", Message: fmt.Sprintf("method %q for struct %q should be placed before method %q", otherMethod.Name, structSpec.Name, method.Name), - } + }) +} + +func reportUnexportedFuncBeforeExportedFunc(pass *analysis.Pass, unexportedFunc, exportedFunc *ast.FuncDecl) { + pass.Report(analysis.Diagnostic{ + Pos: unexportedFunc.Pos(), + URL: "https://github.com/manuelarte/funcorder?tab=readme-ov-file#check-exported-functions-are-placed-before-unexported-functions", //nolint:lll // url + Message: fmt.Sprintf("unexported function %q should be placed after the exported function %q", + unexportedFunc.Name, exportedFunc.Name), + }) } diff --git a/vendor/github.com/manuelarte/funcorder/internal/struct_constructor.go b/vendor/github.com/manuelarte/funcorder/internal/struct_constructor.go index fc4b252d..c036fc13 100644 --- a/vendor/github.com/manuelarte/funcorder/internal/struct_constructor.go +++ b/vendor/github.com/manuelarte/funcorder/internal/struct_constructor.go @@ -2,36 +2,47 @@ package internal import ( "go/ast" + "strings" ) type StructConstructor struct { - constructor *ast.FuncDecl - structReturn *ast.Ident + Constructor *ast.FuncDecl + StructReturn *ast.Ident } -func NewStructConstructor(funcDec *ast.FuncDecl) (StructConstructor, bool) { - if !FuncCanBeConstructor(funcDec) { - return StructConstructor{}, false +func NewStructConstructor(funcDec *ast.FuncDecl) *StructConstructor { + if !funcCanBeConstructor(funcDec) { + return nil } expr := funcDec.Type.Results.List[0].Type - returnType, ok := GetIdent(expr) - if !ok { - return StructConstructor{}, false + returnType := getIdent(expr) + if returnType == nil { + return nil } - return StructConstructor{ - constructor: funcDec, - structReturn: returnType, - }, true + return &StructConstructor{ + Constructor: funcDec, + StructReturn: returnType, + } } -// GetStructReturn Return the struct linked to this "constructor". -func (sc StructConstructor) GetStructReturn() *ast.Ident { - return sc.structReturn -} +func funcCanBeConstructor(n *ast.FuncDecl) bool { + if !n.Name.IsExported() || n.Recv != nil { + return false + } + + if n.Type.Results == nil || len(n.Type.Results.List) == 0 { + return false + } + + for _, prefix := range []string{"new", "must"} { + if strings.HasPrefix(strings.ToLower(n.Name.Name), prefix) && + len(n.Name.Name) > len(prefix) { // TODO(ldez): bug if the name is just `New`. + return true + } + } -func (sc StructConstructor) GetConstructor() *ast.FuncDecl { - return sc.constructor + return false } diff --git a/vendor/github.com/manuelarte/funcorder/internal/structholder.go b/vendor/github.com/manuelarte/funcorder/internal/structholder.go index 424b2ddd..30042211 100644 --- a/vendor/github.com/manuelarte/funcorder/internal/structholder.go +++ b/vendor/github.com/manuelarte/funcorder/internal/structholder.go @@ -3,21 +3,13 @@ package internal import ( "cmp" "go/ast" - "go/token" "slices" "golang.org/x/tools/go/analysis" ) -type ( - ExportedMethods []*ast.FuncDecl - UnexportedMethods []*ast.FuncDecl -) - // StructHolder contains all the information around a Go struct. type StructHolder struct { - // The fileset - Fset *token.FileSet // The features to be analyzed Features Feature @@ -31,59 +23,42 @@ type StructHolder struct { StructMethods []*ast.FuncDecl } -func (sh *StructHolder) AddConstructor(fn *ast.FuncDecl) { - sh.Constructors = append(sh.Constructors, fn) -} - -func (sh *StructHolder) AddMethod(fn *ast.FuncDecl) { - sh.StructMethods = append(sh.StructMethods, fn) -} - // Analyze applies the linter to the struct holder. -func (sh *StructHolder) Analyze() []analysis.Diagnostic { +func (sh *StructHolder) Analyze(pass *analysis.Pass) { // TODO maybe sort constructors and then report also, like NewXXX before MustXXX slices.SortFunc(sh.StructMethods, func(a, b *ast.FuncDecl) int { return cmp.Compare(a.Pos(), b.Pos()) }) - var reports []analysis.Diagnostic + // TODO also check that the methods are declared after the struct if sh.Features.IsEnabled(ConstructorCheck) { - reports = append(reports, sh.analyzeConstructor()...) + sh.analyzeConstructor(pass) } if sh.Features.IsEnabled(StructMethodCheck) { - reports = append(reports, sh.analyzeStructMethod()...) + sh.analyzeStructMethod(pass) } - - // TODO also check that the methods are declared after the struct - return reports } -func (sh *StructHolder) analyzeConstructor() []analysis.Diagnostic { - var reports []analysis.Diagnostic - +func (sh *StructHolder) analyzeConstructor(pass *analysis.Pass) { for i, constructor := range sh.Constructors { if constructor.Pos() < sh.Struct.Pos() { - reports = append(reports, NewConstructorNotAfterStructType(sh.Struct, constructor)) + reportConstructorNotAfterStructType(pass, sh.Struct, constructor) } if len(sh.StructMethods) > 0 && constructor.Pos() > sh.StructMethods[0].Pos() { - reports = append(reports, NewConstructorNotBeforeStructMethod(sh.Struct, constructor, sh.StructMethods[0])) + reportConstructorNotBeforeStructMethod(pass, sh.Struct, constructor, sh.StructMethods[0]) } if sh.Features.IsEnabled(AlphabeticalCheck) && i < len(sh.Constructors)-1 && sh.Constructors[i].Name.Name > sh.Constructors[i+1].Name.Name { - reports = append(reports, - NewAdjacentConstructorsNotSortedAlphabetically(sh.Struct, sh.Constructors[i], sh.Constructors[i+1]), - ) + reportAdjacentConstructorsNotSortedAlphabetically(pass, sh.Struct, sh.Constructors[i], sh.Constructors[i+1]) } } - - return reports } -func (sh *StructHolder) analyzeStructMethod() []analysis.Diagnostic { +func (sh *StructHolder) analyzeStructMethod(pass *analysis.Pass) { var lastExportedMethod *ast.FuncDecl for _, m := range sh.StructMethods { @@ -100,42 +75,46 @@ func (sh *StructHolder) analyzeStructMethod() []analysis.Diagnostic { } } - var reports []analysis.Diagnostic - if lastExportedMethod != nil { for _, m := range sh.StructMethods { if m.Name.IsExported() || m.Pos() >= lastExportedMethod.Pos() { continue } - reports = append(reports, NewUnexportedMethodBeforeExportedForStruct(sh.Struct, m, lastExportedMethod)) + reportUnexportedMethodBeforeExportedForStruct(pass, sh.Struct, m, lastExportedMethod) } } if sh.Features.IsEnabled(AlphabeticalCheck) { - exported, unexported := SplitExportedUnexported(sh.StructMethods) - reports = slices.Concat(reports, - sortDiagnostics(sh.Struct, exported), - sortDiagnostics(sh.Struct, unexported), - ) + exported, unexported := splitExportedUnexported(sh.StructMethods) + sh.sortDiagnostics(pass, exported) + sh.sortDiagnostics(pass, unexported) } - - return reports } -func sortDiagnostics(typeSpec *ast.TypeSpec, funcDecls []*ast.FuncDecl) []analysis.Diagnostic { - var reports []analysis.Diagnostic - +func (sh *StructHolder) sortDiagnostics(pass *analysis.Pass, funcDecls []*ast.FuncDecl) { for i := range funcDecls { if i >= len(funcDecls)-1 { continue } if funcDecls[i].Name.Name > funcDecls[i+1].Name.Name { - reports = append(reports, - NewAdjacentStructMethodsNotSortedAlphabetically(typeSpec, funcDecls[i], funcDecls[i+1])) + reportAdjacentStructMethodsNotSortedAlphabetically(pass, sh.Struct, funcDecls[i], funcDecls[i+1]) + } + } +} + +// splitExportedUnexported split functions/methods based on whether they are exported or not. +// +//nolint:nonamedreturns // names serve as documentation +func splitExportedUnexported(funcDecls []*ast.FuncDecl) (exported, unexported []*ast.FuncDecl) { + for _, f := range funcDecls { + if f.Name.IsExported() { + exported = append(exported, f) + } else { + unexported = append(unexported, f) } } - return reports + return exported, unexported } diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go index 2df7b859..426a409c 100644 --- a/vendor/github.com/mattn/go-colorable/colorable_windows.go +++ b/vendor/github.com/mattn/go-colorable/colorable_windows.go @@ -5,13 +5,13 @@ package colorable import ( "bytes" + syscall "golang.org/x/sys/windows" "io" "math" "os" "strconv" "strings" "sync" - syscall "golang.org/x/sys/windows" "unsafe" "github.com/mattn/go-isatty" @@ -93,6 +93,7 @@ type writer struct { handle syscall.Handle althandle syscall.Handle oldattr word + curattr word oldpos coord rest bytes.Buffer mutex sync.Mutex @@ -112,7 +113,7 @@ func NewColorable(file *os.File) io.Writer { var csbi consoleScreenBufferInfo handle := syscall.Handle(file.Fd()) procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - return &writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} + return &writer{out: file, handle: handle, oldattr: csbi.attributes, curattr: csbi.attributes, oldpos: coord{0, 0}} } return file } @@ -438,7 +439,11 @@ func (w *writer) Write(data []byte) (n int, err error) { w.mutex.Lock() defer w.mutex.Unlock() var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + + if w.rest.Len() == 0 && bytes.IndexByte(data, 0x1b) == -1 { + w.out.Write(data) + return len(data), nil + } handle := w.handle @@ -517,7 +522,7 @@ loop: w.rest.Reset() break } - buf.Write([]byte(string(c))) + buf.WriteByte(c) } if m == 0 { break loop @@ -678,11 +683,11 @@ loop: procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) case 'm': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - attr := csbi.attributes + attr := w.curattr cs := buf.String() if cs == "" { procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) + w.curattr = w.oldattr continue } token := strings.Split(cs, ";") @@ -814,9 +819,12 @@ loop: attr |= backgroundBlue } } - procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) } } + if attr != w.curattr { + procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) + w.curattr = attr + } case 'h': var ci consoleCursorInfo cs := buf.String() @@ -834,6 +842,8 @@ loop: w.althandle = syscall.Handle(h) if w.althandle != 0 { handle = w.althandle + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.curattr = csbi.attributes } } } @@ -853,6 +863,8 @@ loop: syscall.CloseHandle(w.althandle) w.althandle = 0 handle = w.handle + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.curattr = csbi.attributes } } case 's': diff --git a/vendor/github.com/mattn/go-runewidth/SECURITY.md b/vendor/github.com/mattn/go-runewidth/SECURITY.md new file mode 100644 index 00000000..a6898ee7 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/SECURITY.md @@ -0,0 +1,25 @@ +# Security Policy + +## Supported Versions + +The following versions of go-runewidth are currently supported with +security updates. + +| Version | Supported | +| -------- | ------------------ | +| 0.0.23 | :white_check_mark: | +| < 0.0.23 | :x: | + +## Reporting a Vulnerability + +If you discover a security vulnerability in go-runewidth, please report it +privately via GitHub's "Report a vulnerability" feature on the Security tab +of the repository (https://github.com/mattn/go-runewidth/security), or by +emailing the maintainer at mattn.jp@gmail.com. + +Please include a description of the issue, reproduction steps, and the +affected version. You can expect an initial response within one week. If +the vulnerability is accepted, a fix will be prepared and a new release +will be published; you will be credited in the release notes unless you +request otherwise. If the report is declined, you will receive an +explanation of the reasoning. diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go index 0edabac3..6b958fdd 100644 --- a/vendor/github.com/mattn/go-runewidth/runewidth.go +++ b/vendor/github.com/mattn/go-runewidth/runewidth.go @@ -2,7 +2,9 @@ package runewidth import ( "os" + "sort" "strings" + "unicode/utf8" "github.com/clipperhouse/uax29/v2/graphemes" ) @@ -23,10 +25,54 @@ var ( } ) +var ( + zerowidth table // combining + nonprint merged for faster zero-width lookup + widewidth table // ambiguous + doublewidth merged for EA path + eastAsianWidth widthTable + eastAsianWidth0 [0x300]byte +) + func init() { + zerowidth = mergeIntervals(combining, nonprint) + widewidth = mergeIntervals(ambiguous, doublewidth) + eastAsianWidth = makeWidthTable(zerowidth, widewidth) + for r := range eastAsianWidth0 { + eastAsianWidth0[r] = byte(runeWidthEastAsian(rune(r))) + } handleEnv() } +func mergeIntervals(t1, t2 table) table { + merged := make(table, 0, len(t1)+len(t2)) + i, j := 0, 0 + for i < len(t1) && j < len(t2) { + if t1[i].first <= t2[j].first { + merged = append(merged, t1[i]) + i++ + } else { + merged = append(merged, t2[j]) + j++ + } + } + merged = append(merged, t1[i:]...) + merged = append(merged, t2[j:]...) + if len(merged) == 0 { + return merged + } + result := merged[:1] + for _, iv := range merged[1:] { + last := &result[len(result)-1] + if iv.first <= last.last+1 { + if iv.last > last.last { + last.last = iv.last + } + } else { + result = append(result, iv) + } + } + return result +} + func handleEnv() { env := os.Getenv("RUNEWIDTH_EASTASIAN") if env == "" { @@ -51,15 +97,14 @@ type interval struct { type table []interval -func inTables(r rune, ts ...table) bool { - for _, t := range ts { - if inTable(r, t) { - return true - } - } - return false +type widthInterval struct { + first rune + last rune + width byte } +type widthTable []widthInterval + func inTable(r rune, t table) bool { if r < t[0].first { return false @@ -86,6 +131,71 @@ func inTable(r rune, t table) bool { return false } +func makeWidthTable(zero, two table) widthTable { + wt := make(widthTable, 0, len(zero)+len(two)) + zi := 0 + for _, iv := range two { + start := iv.first + for zi < len(zero) && zero[zi].last < start { + zi++ + } + for i := zi; i < len(zero) && zero[i].first <= iv.last; i++ { + if start < zero[i].first { + wt = append(wt, widthInterval{start, zero[i].first - 1, 2}) + } + if start <= zero[i].last { + start = zero[i].last + 1 + } + if start > iv.last { + break + } + } + if start <= iv.last { + wt = append(wt, widthInterval{start, iv.last, 2}) + } + } + for _, iv := range zero { + wt = append(wt, widthInterval{iv.first, iv.last, 0}) + } + sort.Slice(wt, func(i, j int) bool { + return wt[i].first < wt[j].first + }) + return wt +} + +func inWidthTable(r rune, t widthTable) (int, bool) { + if r < t[0].first { + return 0, false + } + if r > t[len(t)-1].last { + return 0, false + } + + bot := 0 + top := len(t) - 1 + for top >= bot { + mid := (bot + top) >> 1 + + switch { + case t[mid].last < r: + bot = mid + 1 + case t[mid].first > r: + top = mid - 1 + default: + return int(t[mid].width), true + } + } + + return 0, false +} + +func runeWidthEastAsian(r rune) int { + if w, ok := inWidthTable(r, eastAsianWidth); ok { + return w + } + return 1 +} + var private = table{ {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, } @@ -123,36 +233,35 @@ func (c *Condition) RuneWidth(r rune) int { } // optimized version, verified by TestRuneWidthChecksums() if !c.EastAsianWidth { - switch { - case r < 0x20: - return 0 - case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint + if r < 0x20 { return 0 - case r < 0x300: - return 1 - case inTable(r, narrow): - return 1 - case inTables(r, nonprint, combining): + } + if (r >= 0x7F && r <= 0x9F) || r == 0xAD { // nonprint return 0 - case inTable(r, doublewidth): - return 2 - default: + } + if r < 0x300 { return 1 } - } else { switch { - case inTables(r, nonprint, combining): + case inTable(r, zerowidth): return 0 - case inTable(r, narrow): - return 1 - case inTables(r, ambiguous, doublewidth): - return 2 - case !c.StrictEmojiNeutral && inTables(r, ambiguous, emoji, narrow): + case inTable(r, doublewidth): return 2 default: return 1 } } + + if r < 0x300 { + return int(eastAsianWidth0[r]) + } + if w, ok := inWidthTable(r, eastAsianWidth); ok { + return w + } + if !c.StrictEmojiNeutral && inTable(r, emoji) { + return 2 + } + return 1 } // CreateLUT will create an in-memory lookup table of 557056 bytes for faster operation. @@ -178,6 +287,33 @@ func (c *Condition) CreateLUT() { // StringWidth return width as you can see func (c *Condition) StringWidth(s string) (width int) { + if len(s) == 1 { + b := s[0] + if b < 0x20 || b == 0x7F { + return 0 + } + return 1 + } + if len(s) > 0 && len(s) <= utf8.UTFMax { + r, size := utf8.DecodeRuneInString(s) + if size == len(s) { + return c.RuneWidth(r) + } + } + // ASCII fast path: no grapheme clustering needed for pure ASCII + for i := 0; i < len(s); i++ { + b := s[i] + if b >= 0x80 { + goto graphemes + } + if b >= 0x20 && b != 0x7F { + width++ + } + } + return + +graphemes: + width = 0 g := graphemes.FromString(s) for g.Next() { var chWidth int @@ -257,24 +393,25 @@ func (c *Condition) TruncateLeft(s string, w int, prefix string) string { // Wrap return string wrapped with w cells func (c *Condition) Wrap(s string, w int) string { width := 0 - out := "" + var out strings.Builder + out.Grow(len(s) + len(s)/w + 1) for _, r := range s { cw := c.RuneWidth(r) if r == '\n' { - out += string(r) + out.WriteRune(r) width = 0 continue } else if width+cw > w { - out += "\n" + out.WriteByte('\n') width = 0 - out += string(r) + out.WriteRune(r) width += cw continue } - out += string(r) + out.WriteRune(r) width += cw } - return out + return out.String() } // FillLeft return string filled in left by spaces in w cells @@ -313,7 +450,12 @@ func RuneWidth(r rune) int { // IsAmbiguousWidth returns whether is ambiguous width or not. func IsAmbiguousWidth(r rune) bool { - return inTables(r, private, ambiguous) + return inTable(r, private) || inTable(r, ambiguous) +} + +// IsCombiningWidth returns whether is combining width or not. +func IsCombiningWidth(r rune) bool { + return inTable(r, combining) } // IsNeutralWidth returns whether is neutral width or not. diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_table.go b/vendor/github.com/mattn/go-runewidth/runewidth_table.go index ad025ad5..cdd003e6 100644 --- a/vendor/github.com/mattn/go-runewidth/runewidth_table.go +++ b/vendor/github.com/mattn/go-runewidth/runewidth_table.go @@ -5,47 +5,50 @@ package runewidth var combining = table{ {0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3}, {0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0CF3, 0x0CF3}, - {0x0D00, 0x0D01}, {0x135D, 0x135F}, {0x1A7F, 0x1A7F}, - {0x1AB0, 0x1ACE}, {0x1B6B, 0x1B73}, {0x1DC0, 0x1DFF}, + {0x0D00, 0x0D01}, {0x135D, 0x135F}, {0x180B, 0x180D}, + {0x180F, 0x180F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1ADD}, + {0x1AE0, 0x1AEB}, {0x1B6B, 0x1B73}, {0x1DC0, 0x1DFF}, {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF}, {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1}, - {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A}, - {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x10F82, 0x10F85}, - {0x11300, 0x11301}, {0x1133B, 0x1133C}, {0x11366, 0x1136C}, - {0x11370, 0x11374}, {0x16AF0, 0x16AF4}, {0x1CF00, 0x1CF2D}, - {0x1CF30, 0x1CF46}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172}, - {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, - {0x1D242, 0x1D244}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, - {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, - {0x1E08F, 0x1E08F}, {0x1E8D0, 0x1E8D6}, + {0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, + {0x10376, 0x1037A}, {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, + {0x10F82, 0x10F85}, {0x11300, 0x11301}, {0x1133B, 0x1133C}, + {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x16AF0, 0x16AF4}, + {0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1D165, 0x1D169}, + {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, + {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1E000, 0x1E006}, + {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, + {0x1E026, 0x1E02A}, {0x1E08F, 0x1E08F}, {0x1E8D0, 0x1E8D6}, + {0xE0100, 0xE01EF}, } var doublewidth = table{ {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, - {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, - {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, - {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, - {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, - {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, - {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, - {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, - {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, - {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, - {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, - {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x303E}, - {0x3041, 0x3096}, {0x3099, 0x30FF}, {0x3105, 0x312F}, - {0x3131, 0x318E}, {0x3190, 0x31E3}, {0x31EF, 0x321E}, - {0x3220, 0x3247}, {0x3250, 0x4DBF}, {0x4E00, 0xA48C}, - {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, - {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, - {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, - {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, {0x16FF0, 0x16FF1}, - {0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x18D00, 0x18D08}, - {0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB}, {0x1AFFD, 0x1AFFE}, - {0x1B000, 0x1B122}, {0x1B132, 0x1B132}, {0x1B150, 0x1B152}, - {0x1B155, 0x1B155}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, + {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2630, 0x2637}, + {0x2648, 0x2653}, {0x267F, 0x267F}, {0x268A, 0x268F}, + {0x2693, 0x2693}, {0x26A1, 0x26A1}, {0x26AA, 0x26AB}, + {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, {0x26CE, 0x26CE}, + {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, {0x26F2, 0x26F3}, + {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, {0x26FD, 0x26FD}, + {0x2705, 0x2705}, {0x270A, 0x270B}, {0x2728, 0x2728}, + {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, + {0x2757, 0x2757}, {0x2795, 0x2797}, {0x27B0, 0x27B0}, + {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, + {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3}, + {0x2F00, 0x2FD5}, {0x2FF0, 0x303E}, {0x3041, 0x3096}, + {0x3099, 0x30FF}, {0x3105, 0x312F}, {0x3131, 0x318E}, + {0x3190, 0x31E5}, {0x31EF, 0x321E}, {0x3220, 0x3247}, + {0x3250, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, + {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, + {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, + {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, + {0x16FF0, 0x16FF6}, {0x17000, 0x18CD5}, {0x18CFF, 0x18D1E}, + {0x18D80, 0x18DF2}, {0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB}, + {0x1AFFD, 0x1AFFE}, {0x1B000, 0x1B122}, {0x1B132, 0x1B132}, + {0x1B150, 0x1B152}, {0x1B155, 0x1B155}, {0x1B164, 0x1B167}, + {0x1B170, 0x1B2FB}, {0x1D300, 0x1D356}, {0x1D360, 0x1D376}, {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, {0x1F260, 0x1F265}, @@ -56,12 +59,12 @@ var doublewidth = table{ {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, - {0x1F6D5, 0x1F6D7}, {0x1F6DC, 0x1F6DF}, {0x1F6EB, 0x1F6EC}, + {0x1F6D5, 0x1F6D8}, {0x1F6DC, 0x1F6DF}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, {0x1F7F0, 0x1F7F0}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F9FF}, - {0x1FA70, 0x1FA7C}, {0x1FA80, 0x1FA88}, {0x1FA90, 0x1FABD}, - {0x1FABF, 0x1FAC5}, {0x1FACE, 0x1FADB}, {0x1FAE0, 0x1FAE8}, - {0x1FAF0, 0x1FAF8}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, + {0x1FA70, 0x1FA7C}, {0x1FA80, 0x1FA8A}, {0x1FA8E, 0x1FAC6}, + {0x1FAC8, 0x1FAC8}, {0x1FACD, 0x1FADC}, {0x1FADF, 0x1FAEA}, + {0x1FAEF, 0x1FAF8}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, } var ambiguous = table{ @@ -121,10 +124,9 @@ var ambiguous = table{ {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, - {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, - {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, - {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, - {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, + {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, {0x1F110, 0x1F12D}, + {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, {0x1F18F, 0x1F190}, + {0x1F19B, 0x1F1AC}, {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, } var narrow = table{ {0x0020, 0x007E}, {0x00A2, 0x00A3}, {0x00A5, 0x00A6}, @@ -159,115 +161,116 @@ var neutral = table{ {0x0600, 0x070D}, {0x070F, 0x074A}, {0x074D, 0x07B1}, {0x07C0, 0x07FA}, {0x07FD, 0x082D}, {0x0830, 0x083E}, {0x0840, 0x085B}, {0x085E, 0x085E}, {0x0860, 0x086A}, - {0x0870, 0x088E}, {0x0890, 0x0891}, {0x0898, 0x0983}, - {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, - {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, - {0x09BC, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CE}, - {0x09D7, 0x09D7}, {0x09DC, 0x09DD}, {0x09DF, 0x09E3}, - {0x09E6, 0x09FE}, {0x0A01, 0x0A03}, {0x0A05, 0x0A0A}, - {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, - {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, - {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, {0x0A47, 0x0A48}, - {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, {0x0A59, 0x0A5C}, - {0x0A5E, 0x0A5E}, {0x0A66, 0x0A76}, {0x0A81, 0x0A83}, - {0x0A85, 0x0A8D}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, - {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, - {0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, - {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3}, {0x0AE6, 0x0AF1}, - {0x0AF9, 0x0AFF}, {0x0B01, 0x0B03}, {0x0B05, 0x0B0C}, - {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, - {0x0B32, 0x0B33}, {0x0B35, 0x0B39}, {0x0B3C, 0x0B44}, - {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B55, 0x0B57}, - {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63}, {0x0B66, 0x0B77}, - {0x0B82, 0x0B83}, {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, - {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, - {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, - {0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, - {0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, - {0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C}, {0x0C0E, 0x0C10}, - {0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, {0x0C3C, 0x0C44}, - {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, - {0x0C58, 0x0C5A}, {0x0C5D, 0x0C5D}, {0x0C60, 0x0C63}, - {0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, - {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, - {0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, - {0x0CD5, 0x0CD6}, {0x0CDD, 0x0CDE}, {0x0CE0, 0x0CE3}, - {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF3}, {0x0D00, 0x0D0C}, - {0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, {0x0D46, 0x0D48}, - {0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, - {0x0D81, 0x0D83}, {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, - {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, - {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, - {0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4}, - {0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82}, - {0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3}, - {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4}, - {0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECE}, {0x0ED0, 0x0ED9}, - {0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C}, - {0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC}, - {0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7}, - {0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248}, - {0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258}, - {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D}, - {0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE}, - {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6}, - {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A}, - {0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5}, - {0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8}, - {0x1700, 0x1715}, {0x171F, 0x1736}, {0x1740, 0x1753}, - {0x1760, 0x176C}, {0x176E, 0x1770}, {0x1772, 0x1773}, - {0x1780, 0x17DD}, {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, - {0x1800, 0x1819}, {0x1820, 0x1878}, {0x1880, 0x18AA}, - {0x18B0, 0x18F5}, {0x1900, 0x191E}, {0x1920, 0x192B}, - {0x1930, 0x193B}, {0x1940, 0x1940}, {0x1944, 0x196D}, - {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, - {0x19D0, 0x19DA}, {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, - {0x1A60, 0x1A7C}, {0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, - {0x1AA0, 0x1AAD}, {0x1AB0, 0x1ACE}, {0x1B00, 0x1B4C}, - {0x1B50, 0x1B7E}, {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, - {0x1C3B, 0x1C49}, {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, - {0x1CBD, 0x1CC7}, {0x1CD0, 0x1CFA}, {0x1D00, 0x1F15}, - {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, - {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, - {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, - {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB}, - {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE}, - {0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017}, - {0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023}, - {0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034}, - {0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064}, - {0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080}, - {0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8}, - {0x20AA, 0x20AB}, {0x20AD, 0x20C0}, {0x20D0, 0x20F0}, - {0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108}, - {0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120}, - {0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152}, - {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F}, - {0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7}, - {0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6}, - {0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206}, - {0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210}, - {0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C}, - {0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226}, - {0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B}, - {0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251}, - {0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269}, - {0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285}, - {0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4}, - {0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319}, - {0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF}, - {0x23F1, 0x23F2}, {0x23F4, 0x2426}, {0x2440, 0x244A}, - {0x24EA, 0x24EA}, {0x254C, 0x254F}, {0x2574, 0x257F}, - {0x2590, 0x2591}, {0x2596, 0x259F}, {0x25A2, 0x25A2}, - {0x25AA, 0x25B1}, {0x25B4, 0x25B5}, {0x25B8, 0x25BB}, - {0x25BE, 0x25BF}, {0x25C2, 0x25C5}, {0x25C9, 0x25CA}, - {0x25CC, 0x25CD}, {0x25D2, 0x25E1}, {0x25E6, 0x25EE}, - {0x25F0, 0x25FC}, {0x25FF, 0x2604}, {0x2607, 0x2608}, - {0x260A, 0x260D}, {0x2610, 0x2613}, {0x2616, 0x261B}, - {0x261D, 0x261D}, {0x261F, 0x263F}, {0x2641, 0x2641}, - {0x2643, 0x2647}, {0x2654, 0x265F}, {0x2662, 0x2662}, - {0x2666, 0x2666}, {0x266B, 0x266B}, {0x266E, 0x266E}, - {0x2670, 0x267E}, {0x2680, 0x2692}, {0x2694, 0x269D}, + {0x0870, 0x0891}, {0x0897, 0x0983}, {0x0985, 0x098C}, + {0x098F, 0x0990}, {0x0993, 0x09A8}, {0x09AA, 0x09B0}, + {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09BC, 0x09C4}, + {0x09C7, 0x09C8}, {0x09CB, 0x09CE}, {0x09D7, 0x09D7}, + {0x09DC, 0x09DD}, {0x09DF, 0x09E3}, {0x09E6, 0x09FE}, + {0x0A01, 0x0A03}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, + {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, + {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C}, + {0x0A3E, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, + {0x0A51, 0x0A51}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, + {0x0A66, 0x0A76}, {0x0A81, 0x0A83}, {0x0A85, 0x0A8D}, + {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, + {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5}, + {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0}, + {0x0AE0, 0x0AE3}, {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF}, + {0x0B01, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, + {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, + {0x0B35, 0x0B39}, {0x0B3C, 0x0B44}, {0x0B47, 0x0B48}, + {0x0B4B, 0x0B4D}, {0x0B55, 0x0B57}, {0x0B5C, 0x0B5D}, + {0x0B5F, 0x0B63}, {0x0B66, 0x0B77}, {0x0B82, 0x0B83}, + {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, + {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, + {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, + {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, + {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA}, + {0x0C00, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, + {0x0C2A, 0x0C39}, {0x0C3C, 0x0C44}, {0x0C46, 0x0C48}, + {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, + {0x0C5C, 0x0C5D}, {0x0C60, 0x0C63}, {0x0C66, 0x0C6F}, + {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8}, + {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, {0x0CBC, 0x0CC4}, + {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, + {0x0CDC, 0x0CDE}, {0x0CE0, 0x0CE3}, {0x0CE6, 0x0CEF}, + {0x0CF1, 0x0CF3}, {0x0D00, 0x0D0C}, {0x0D0E, 0x0D10}, + {0x0D12, 0x0D44}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4F}, + {0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, {0x0D81, 0x0D83}, + {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB}, + {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA}, + {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, + {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4}, {0x0E01, 0x0E3A}, + {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84}, + {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3}, {0x0EA5, 0x0EA5}, + {0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6}, + {0x0EC8, 0x0ECE}, {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF}, + {0x0F00, 0x0F47}, {0x0F49, 0x0F6C}, {0x0F71, 0x0F97}, + {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC}, {0x0FCE, 0x0FDA}, + {0x1000, 0x10C5}, {0x10C7, 0x10C7}, {0x10CD, 0x10CD}, + {0x10D0, 0x10FF}, {0x1160, 0x1248}, {0x124A, 0x124D}, + {0x1250, 0x1256}, {0x1258, 0x1258}, {0x125A, 0x125D}, + {0x1260, 0x1288}, {0x128A, 0x128D}, {0x1290, 0x12B0}, + {0x12B2, 0x12B5}, {0x12B8, 0x12BE}, {0x12C0, 0x12C0}, + {0x12C2, 0x12C5}, {0x12C8, 0x12D6}, {0x12D8, 0x1310}, + {0x1312, 0x1315}, {0x1318, 0x135A}, {0x135D, 0x137C}, + {0x1380, 0x1399}, {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, + {0x1400, 0x169C}, {0x16A0, 0x16F8}, {0x1700, 0x1715}, + {0x171F, 0x1736}, {0x1740, 0x1753}, {0x1760, 0x176C}, + {0x176E, 0x1770}, {0x1772, 0x1773}, {0x1780, 0x17DD}, + {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x180A}, + {0x180E, 0x180E}, {0x1810, 0x1819}, {0x1820, 0x1878}, + {0x1880, 0x18AA}, {0x18B0, 0x18F5}, {0x1900, 0x191E}, + {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1940, 0x1940}, + {0x1944, 0x196D}, {0x1970, 0x1974}, {0x1980, 0x19AB}, + {0x19B0, 0x19C9}, {0x19D0, 0x19DA}, {0x19DE, 0x1A1B}, + {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A89}, + {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD}, {0x1AB0, 0x1ADD}, + {0x1AE0, 0x1AEB}, {0x1B00, 0x1B4C}, {0x1B4E, 0x1BF3}, + {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49}, {0x1C4D, 0x1C8A}, + {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7}, {0x1CD0, 0x1CFA}, + {0x1D00, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, + {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, + {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, + {0x1F80, 0x1FB4}, {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, + {0x1FD6, 0x1FDB}, {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, + {0x1FF6, 0x1FFE}, {0x2000, 0x200F}, {0x2011, 0x2012}, + {0x2017, 0x2017}, {0x201A, 0x201B}, {0x201E, 0x201F}, + {0x2023, 0x2023}, {0x2028, 0x202F}, {0x2031, 0x2031}, + {0x2034, 0x2034}, {0x2036, 0x203A}, {0x203C, 0x203D}, + {0x203F, 0x2064}, {0x2066, 0x2071}, {0x2075, 0x207E}, + {0x2080, 0x2080}, {0x2085, 0x208E}, {0x2090, 0x209C}, + {0x20A0, 0x20A8}, {0x20AA, 0x20AB}, {0x20AD, 0x20C1}, + {0x20D0, 0x20F0}, {0x2100, 0x2102}, {0x2104, 0x2104}, + {0x2106, 0x2108}, {0x210A, 0x2112}, {0x2114, 0x2115}, + {0x2117, 0x2120}, {0x2123, 0x2125}, {0x2127, 0x212A}, + {0x212C, 0x2152}, {0x2155, 0x215A}, {0x215F, 0x215F}, + {0x216C, 0x216F}, {0x217A, 0x2188}, {0x218A, 0x218B}, + {0x219A, 0x21B7}, {0x21BA, 0x21D1}, {0x21D3, 0x21D3}, + {0x21D5, 0x21E6}, {0x21E8, 0x21FF}, {0x2201, 0x2201}, + {0x2204, 0x2206}, {0x2209, 0x220A}, {0x220C, 0x220E}, + {0x2210, 0x2210}, {0x2212, 0x2214}, {0x2216, 0x2219}, + {0x221B, 0x221C}, {0x2221, 0x2222}, {0x2224, 0x2224}, + {0x2226, 0x2226}, {0x222D, 0x222D}, {0x222F, 0x2233}, + {0x2238, 0x223B}, {0x223E, 0x2247}, {0x2249, 0x224B}, + {0x224D, 0x2251}, {0x2253, 0x225F}, {0x2262, 0x2263}, + {0x2268, 0x2269}, {0x226C, 0x226D}, {0x2270, 0x2281}, + {0x2284, 0x2285}, {0x2288, 0x2294}, {0x2296, 0x2298}, + {0x229A, 0x22A4}, {0x22A6, 0x22BE}, {0x22C0, 0x2311}, + {0x2313, 0x2319}, {0x231C, 0x2328}, {0x232B, 0x23E8}, + {0x23ED, 0x23EF}, {0x23F1, 0x23F2}, {0x23F4, 0x2429}, + {0x2440, 0x244A}, {0x24EA, 0x24EA}, {0x254C, 0x254F}, + {0x2574, 0x257F}, {0x2590, 0x2591}, {0x2596, 0x259F}, + {0x25A2, 0x25A2}, {0x25AA, 0x25B1}, {0x25B4, 0x25B5}, + {0x25B8, 0x25BB}, {0x25BE, 0x25BF}, {0x25C2, 0x25C5}, + {0x25C9, 0x25CA}, {0x25CC, 0x25CD}, {0x25D2, 0x25E1}, + {0x25E6, 0x25EE}, {0x25F0, 0x25FC}, {0x25FF, 0x2604}, + {0x2607, 0x2608}, {0x260A, 0x260D}, {0x2610, 0x2613}, + {0x2616, 0x261B}, {0x261D, 0x261D}, {0x261F, 0x262F}, + {0x2638, 0x263F}, {0x2641, 0x2641}, {0x2643, 0x2647}, + {0x2654, 0x265F}, {0x2662, 0x2662}, {0x2666, 0x2666}, + {0x266B, 0x266B}, {0x266E, 0x266E}, {0x2670, 0x267E}, + {0x2680, 0x2689}, {0x2690, 0x2692}, {0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, {0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, {0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709}, {0x270C, 0x2727}, @@ -276,175 +279,210 @@ var neutral = table{ {0x2780, 0x2794}, {0x2798, 0x27AF}, {0x27B1, 0x27BE}, {0x27C0, 0x27E5}, {0x27EE, 0x2984}, {0x2987, 0x2B1A}, {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, {0x2B5A, 0x2B73}, - {0x2B76, 0x2B95}, {0x2B97, 0x2CF3}, {0x2CF9, 0x2D25}, - {0x2D27, 0x2D27}, {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, - {0x2D6F, 0x2D70}, {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6}, - {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, - {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, - {0x2DD8, 0x2DDE}, {0x2DE0, 0x2E5D}, {0x303F, 0x303F}, - {0x4DC0, 0x4DFF}, {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, - {0xA700, 0xA7CA}, {0xA7D0, 0xA7D1}, {0xA7D3, 0xA7D3}, - {0xA7D5, 0xA7D9}, {0xA7F2, 0xA82C}, {0xA830, 0xA839}, - {0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, - {0xA8E0, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD}, - {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36}, - {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2}, - {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, - {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, - {0xAB30, 0xAB6B}, {0xAB70, 0xABED}, {0xABF0, 0xABF9}, - {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF}, - {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36}, - {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, - {0xFB43, 0xFB44}, {0xFB46, 0xFBC2}, {0xFBD3, 0xFD8F}, - {0xFD92, 0xFDC7}, {0xFDCF, 0xFDCF}, {0xFDF0, 0xFDFF}, - {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, - {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B}, - {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D}, - {0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA}, - {0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E}, - {0x10190, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD}, - {0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB}, - {0x10300, 0x10323}, {0x1032D, 0x1034A}, {0x10350, 0x1037A}, - {0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5}, - {0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3}, - {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563}, - {0x1056F, 0x1057A}, {0x1057C, 0x1058A}, {0x1058C, 0x10592}, - {0x10594, 0x10595}, {0x10597, 0x105A1}, {0x105A3, 0x105B1}, - {0x105B3, 0x105B9}, {0x105BB, 0x105BC}, {0x10600, 0x10736}, - {0x10740, 0x10755}, {0x10760, 0x10767}, {0x10780, 0x10785}, - {0x10787, 0x107B0}, {0x107B2, 0x107BA}, {0x10800, 0x10805}, - {0x10808, 0x10808}, {0x1080A, 0x10835}, {0x10837, 0x10838}, - {0x1083C, 0x1083C}, {0x1083F, 0x10855}, {0x10857, 0x1089E}, - {0x108A7, 0x108AF}, {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, - {0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x1093F, 0x1093F}, - {0x10980, 0x109B7}, {0x109BC, 0x109CF}, {0x109D2, 0x10A03}, - {0x10A05, 0x10A06}, {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, - {0x10A19, 0x10A35}, {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, - {0x10A50, 0x10A58}, {0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, - {0x10AEB, 0x10AF6}, {0x10B00, 0x10B35}, {0x10B39, 0x10B55}, - {0x10B58, 0x10B72}, {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, - {0x10BA9, 0x10BAF}, {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, - {0x10CC0, 0x10CF2}, {0x10CFA, 0x10D27}, {0x10D30, 0x10D39}, - {0x10E60, 0x10E7E}, {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD}, - {0x10EB0, 0x10EB1}, {0x10EFD, 0x10F27}, {0x10F30, 0x10F59}, - {0x10F70, 0x10F89}, {0x10FB0, 0x10FCB}, {0x10FE0, 0x10FF6}, - {0x11000, 0x1104D}, {0x11052, 0x11075}, {0x1107F, 0x110C2}, - {0x110CD, 0x110CD}, {0x110D0, 0x110E8}, {0x110F0, 0x110F9}, - {0x11100, 0x11134}, {0x11136, 0x11147}, {0x11150, 0x11176}, - {0x11180, 0x111DF}, {0x111E1, 0x111F4}, {0x11200, 0x11211}, - {0x11213, 0x11241}, {0x11280, 0x11286}, {0x11288, 0x11288}, - {0x1128A, 0x1128D}, {0x1128F, 0x1129D}, {0x1129F, 0x112A9}, - {0x112B0, 0x112EA}, {0x112F0, 0x112F9}, {0x11300, 0x11303}, - {0x11305, 0x1130C}, {0x1130F, 0x11310}, {0x11313, 0x11328}, - {0x1132A, 0x11330}, {0x11332, 0x11333}, {0x11335, 0x11339}, - {0x1133B, 0x11344}, {0x11347, 0x11348}, {0x1134B, 0x1134D}, - {0x11350, 0x11350}, {0x11357, 0x11357}, {0x1135D, 0x11363}, - {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x1145B}, + {0x2B76, 0x2CF3}, {0x2CF9, 0x2D25}, {0x2D27, 0x2D27}, + {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D70}, + {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, + {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, + {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, + {0x2DE0, 0x2E5D}, {0x303F, 0x303F}, {0xA4D0, 0xA62B}, + {0xA640, 0xA6F7}, {0xA700, 0xA7DC}, {0xA7F1, 0xA82C}, + {0xA830, 0xA839}, {0xA840, 0xA877}, {0xA880, 0xA8C5}, + {0xA8CE, 0xA8D9}, {0xA8E0, 0xA953}, {0xA95F, 0xA95F}, + {0xA980, 0xA9CD}, {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, + {0xAA00, 0xAA36}, {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, + {0xAA5C, 0xAAC2}, {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, + {0xAB09, 0xAB0E}, {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, + {0xAB28, 0xAB2E}, {0xAB30, 0xAB6B}, {0xAB70, 0xABED}, + {0xABF0, 0xABF9}, {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, + {0xD800, 0xDFFF}, {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, + {0xFB1D, 0xFB36}, {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, + {0xFB40, 0xFB41}, {0xFB43, 0xFB44}, {0xFB46, 0xFDCF}, + {0xFDF0, 0xFDFF}, {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, + {0xFE76, 0xFEFC}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, + {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, + {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, + {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133}, + {0x10137, 0x1018E}, {0x10190, 0x1019C}, {0x101A0, 0x101A0}, + {0x101D0, 0x101FD}, {0x10280, 0x1029C}, {0x102A0, 0x102D0}, + {0x102E0, 0x102FB}, {0x10300, 0x10323}, {0x1032D, 0x1034A}, + {0x10350, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x103C3}, + {0x103C8, 0x103D5}, {0x10400, 0x1049D}, {0x104A0, 0x104A9}, + {0x104B0, 0x104D3}, {0x104D8, 0x104FB}, {0x10500, 0x10527}, + {0x10530, 0x10563}, {0x1056F, 0x1057A}, {0x1057C, 0x1058A}, + {0x1058C, 0x10592}, {0x10594, 0x10595}, {0x10597, 0x105A1}, + {0x105A3, 0x105B1}, {0x105B3, 0x105B9}, {0x105BB, 0x105BC}, + {0x105C0, 0x105F3}, {0x10600, 0x10736}, {0x10740, 0x10755}, + {0x10760, 0x10767}, {0x10780, 0x10785}, {0x10787, 0x107B0}, + {0x107B2, 0x107BA}, {0x10800, 0x10805}, {0x10808, 0x10808}, + {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C}, + {0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF}, + {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x1091B}, + {0x1091F, 0x10939}, {0x1093F, 0x10959}, {0x10980, 0x109B7}, + {0x109BC, 0x109CF}, {0x109D2, 0x10A03}, {0x10A05, 0x10A06}, + {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35}, + {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, {0x10A50, 0x10A58}, + {0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6}, + {0x10B00, 0x10B35}, {0x10B39, 0x10B55}, {0x10B58, 0x10B72}, + {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF}, + {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, + {0x10CFA, 0x10D27}, {0x10D30, 0x10D39}, {0x10D40, 0x10D65}, + {0x10D69, 0x10D85}, {0x10D8E, 0x10D8F}, {0x10E60, 0x10E7E}, + {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD}, {0x10EB0, 0x10EB1}, + {0x10EC2, 0x10EC7}, {0x10ED0, 0x10ED8}, {0x10EFA, 0x10F27}, + {0x10F30, 0x10F59}, {0x10F70, 0x10F89}, {0x10FB0, 0x10FCB}, + {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x11075}, + {0x1107F, 0x110C2}, {0x110CD, 0x110CD}, {0x110D0, 0x110E8}, + {0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11147}, + {0x11150, 0x11176}, {0x11180, 0x111DF}, {0x111E1, 0x111F4}, + {0x11200, 0x11211}, {0x11213, 0x11241}, {0x11280, 0x11286}, + {0x11288, 0x11288}, {0x1128A, 0x1128D}, {0x1128F, 0x1129D}, + {0x1129F, 0x112A9}, {0x112B0, 0x112EA}, {0x112F0, 0x112F9}, + {0x11300, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, + {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, + {0x11335, 0x11339}, {0x1133B, 0x11344}, {0x11347, 0x11348}, + {0x1134B, 0x1134D}, {0x11350, 0x11350}, {0x11357, 0x11357}, + {0x1135D, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, + {0x11380, 0x11389}, {0x1138B, 0x1138B}, {0x1138E, 0x1138E}, + {0x11390, 0x113B5}, {0x113B7, 0x113C0}, {0x113C2, 0x113C2}, + {0x113C5, 0x113C5}, {0x113C7, 0x113CA}, {0x113CC, 0x113D5}, + {0x113D7, 0x113D8}, {0x113E1, 0x113E2}, {0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7}, {0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD}, {0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B9}, - {0x116C0, 0x116C9}, {0x11700, 0x1171A}, {0x1171D, 0x1172B}, - {0x11730, 0x11746}, {0x11800, 0x1183B}, {0x118A0, 0x118F2}, - {0x118FF, 0x11906}, {0x11909, 0x11909}, {0x1190C, 0x11913}, - {0x11915, 0x11916}, {0x11918, 0x11935}, {0x11937, 0x11938}, - {0x1193B, 0x11946}, {0x11950, 0x11959}, {0x119A0, 0x119A7}, - {0x119AA, 0x119D7}, {0x119DA, 0x119E4}, {0x11A00, 0x11A47}, - {0x11A50, 0x11AA2}, {0x11AB0, 0x11AF8}, {0x11B00, 0x11B09}, - {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45}, - {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7}, - {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09}, - {0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D}, - {0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, {0x11D60, 0x11D65}, - {0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, {0x11D90, 0x11D91}, - {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8}, - {0x11F00, 0x11F10}, {0x11F12, 0x11F3A}, {0x11F3E, 0x11F59}, + {0x116C0, 0x116C9}, {0x116D0, 0x116E3}, {0x11700, 0x1171A}, + {0x1171D, 0x1172B}, {0x11730, 0x11746}, {0x11800, 0x1183B}, + {0x118A0, 0x118F2}, {0x118FF, 0x11906}, {0x11909, 0x11909}, + {0x1190C, 0x11913}, {0x11915, 0x11916}, {0x11918, 0x11935}, + {0x11937, 0x11938}, {0x1193B, 0x11946}, {0x11950, 0x11959}, + {0x119A0, 0x119A7}, {0x119AA, 0x119D7}, {0x119DA, 0x119E4}, + {0x11A00, 0x11A47}, {0x11A50, 0x11AA2}, {0x11AB0, 0x11AF8}, + {0x11B00, 0x11B09}, {0x11B60, 0x11B67}, {0x11BC0, 0x11BE1}, + {0x11BF0, 0x11BF9}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, + {0x11C38, 0x11C45}, {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, + {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, + {0x11D08, 0x11D09}, {0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, + {0x11D3C, 0x11D3D}, {0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, + {0x11D60, 0x11D65}, {0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, + {0x11D90, 0x11D91}, {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, + {0x11DB0, 0x11DDB}, {0x11DE0, 0x11DE9}, {0x11EE0, 0x11EF8}, + {0x11F00, 0x11F10}, {0x11F12, 0x11F3A}, {0x11F3E, 0x11F5A}, {0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, - {0x12F90, 0x12FF2}, {0x13000, 0x13455}, {0x14400, 0x14646}, - {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, - {0x16A6E, 0x16ABE}, {0x16AC0, 0x16AC9}, {0x16AD0, 0x16AED}, - {0x16AF0, 0x16AF5}, {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, - {0x16B5B, 0x16B61}, {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, - {0x16E40, 0x16E9A}, {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, - {0x16F8F, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, - {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, + {0x12F90, 0x12FF2}, {0x13000, 0x13455}, {0x13460, 0x143FA}, + {0x14400, 0x14646}, {0x16100, 0x16139}, {0x16800, 0x16A38}, + {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, {0x16A6E, 0x16ABE}, + {0x16AC0, 0x16AC9}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, + {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, + {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16D40, 0x16D79}, + {0x16E40, 0x16E9A}, {0x16EA0, 0x16EB8}, {0x16EBB, 0x16ED3}, + {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F}, + {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, + {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1CC00, 0x1CCFC}, + {0x1CD00, 0x1CEB3}, {0x1CEBA, 0x1CED0}, {0x1CEE0, 0x1CEF0}, {0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1CF50, 0x1CFC3}, {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D1EA}, {0x1D200, 0x1D245}, {0x1D2C0, 0x1D2D3}, {0x1D2E0, 0x1D2F3}, - {0x1D300, 0x1D356}, {0x1D360, 0x1D378}, {0x1D400, 0x1D454}, - {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2}, - {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, - {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, - {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, - {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, - {0x1D546, 0x1D546}, {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, - {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B}, {0x1DA9B, 0x1DA9F}, - {0x1DAA1, 0x1DAAF}, {0x1DF00, 0x1DF1E}, {0x1DF25, 0x1DF2A}, - {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, - {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E030, 0x1E06D}, - {0x1E08F, 0x1E08F}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D}, - {0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E290, 0x1E2AE}, - {0x1E2C0, 0x1E2F9}, {0x1E2FF, 0x1E2FF}, {0x1E4D0, 0x1E4F9}, - {0x1E7E0, 0x1E7E6}, {0x1E7E8, 0x1E7EB}, {0x1E7ED, 0x1E7EE}, - {0x1E7F0, 0x1E7FE}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6}, - {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, - {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03}, - {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, - {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, - {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, - {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, - {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54}, - {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, - {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, - {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, - {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, - {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, - {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1}, - {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093}, - {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE}, - {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F}, - {0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF}, - {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, - {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, - {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, - {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, - {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, - {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, - {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4}, - {0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F776}, - {0x1F77B, 0x1F7D9}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, + {0x1D377, 0x1D378}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, + {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, + {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, + {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, + {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, + {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, + {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, + {0x1D7CE, 0x1DA8B}, {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, + {0x1DF00, 0x1DF1E}, {0x1DF25, 0x1DF2A}, {0x1E000, 0x1E006}, + {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, + {0x1E026, 0x1E02A}, {0x1E030, 0x1E06D}, {0x1E08F, 0x1E08F}, + {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D}, {0x1E140, 0x1E149}, + {0x1E14E, 0x1E14F}, {0x1E290, 0x1E2AE}, {0x1E2C0, 0x1E2F9}, + {0x1E2FF, 0x1E2FF}, {0x1E4D0, 0x1E4F9}, {0x1E5D0, 0x1E5FA}, + {0x1E5FF, 0x1E5FF}, {0x1E6C0, 0x1E6DE}, {0x1E6E0, 0x1E6F5}, + {0x1E6FE, 0x1E6FF}, {0x1E7E0, 0x1E7E6}, {0x1E7E8, 0x1E7EB}, + {0x1E7ED, 0x1E7EE}, {0x1E7F0, 0x1E7FE}, {0x1E800, 0x1E8C4}, + {0x1E8C7, 0x1E8D6}, {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, + {0x1E95E, 0x1E95F}, {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, + {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, + {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, + {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, + {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, + {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, + {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, + {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, + {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, + {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, + {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, + {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, + {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, + {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, + {0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10F}, + {0x1F12E, 0x1F12F}, {0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, + {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, + {0x1F37D, 0x1F37D}, {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, + {0x1F3D4, 0x1F3DF}, {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, + {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, + {0x1F53E, 0x1F54A}, {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, + {0x1F57B, 0x1F594}, {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, + {0x1F650, 0x1F67F}, {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, + {0x1F6D3, 0x1F6D4}, {0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, + {0x1F700, 0x1F7D9}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, - {0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B}, - {0x1F946, 0x1F946}, {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D}, - {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9}, - {0xE0001, 0xE0001}, {0xE0020, 0xE007F}, + {0x1F8B0, 0x1F8BB}, {0x1F8C0, 0x1F8C1}, {0x1F8D0, 0x1F8D8}, + {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B}, {0x1F946, 0x1F946}, + {0x1FA00, 0x1FA57}, {0x1FA60, 0x1FA6D}, {0x1FB00, 0x1FB92}, + {0x1FB94, 0x1FBFA}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F}, } var emoji = table{ {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122}, {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA}, - {0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388}, - {0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, - {0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, - {0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605}, - {0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705}, - {0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716}, - {0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728}, - {0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747}, - {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, - {0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797}, - {0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, - {0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C}, - {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030}, - {0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299}, - {0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F}, - {0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, - {0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F}, - {0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, - {0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D}, - {0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F}, - {0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, - {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF}, - {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FAFF}, + {0x231A, 0x231B}, {0x2328, 0x2328}, {0x23CF, 0x23CF}, + {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, {0x24C2, 0x24C2}, + {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, {0x25C0, 0x25C0}, + {0x25FB, 0x25FE}, {0x2600, 0x2604}, {0x260E, 0x260E}, + {0x2611, 0x2611}, {0x2614, 0x2615}, {0x2618, 0x2618}, + {0x261D, 0x261D}, {0x2620, 0x2620}, {0x2622, 0x2623}, + {0x2626, 0x2626}, {0x262A, 0x262A}, {0x262E, 0x262F}, + {0x2638, 0x263A}, {0x2640, 0x2640}, {0x2642, 0x2642}, + {0x2648, 0x2653}, {0x265F, 0x2660}, {0x2663, 0x2663}, + {0x2665, 0x2666}, {0x2668, 0x2668}, {0x267B, 0x267B}, + {0x267E, 0x267F}, {0x2692, 0x2697}, {0x2699, 0x2699}, + {0x269B, 0x269C}, {0x26A0, 0x26A1}, {0x26A7, 0x26A7}, + {0x26AA, 0x26AB}, {0x26B0, 0x26B1}, {0x26BD, 0x26BE}, + {0x26C4, 0x26C5}, {0x26C8, 0x26C8}, {0x26CE, 0x26CF}, + {0x26D1, 0x26D1}, {0x26D3, 0x26D4}, {0x26E9, 0x26EA}, + {0x26F0, 0x26F5}, {0x26F7, 0x26FA}, {0x26FD, 0x26FD}, + {0x2702, 0x2702}, {0x2705, 0x2705}, {0x2708, 0x270D}, + {0x270F, 0x270F}, {0x2712, 0x2712}, {0x2714, 0x2714}, + {0x2716, 0x2716}, {0x271D, 0x271D}, {0x2721, 0x2721}, + {0x2728, 0x2728}, {0x2733, 0x2734}, {0x2744, 0x2744}, + {0x2747, 0x2747}, {0x274C, 0x274C}, {0x274E, 0x274E}, + {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2763, 0x2764}, + {0x2795, 0x2797}, {0x27A1, 0x27A1}, {0x27B0, 0x27B0}, + {0x27BF, 0x27BF}, {0x2934, 0x2935}, {0x2B05, 0x2B07}, + {0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, + {0x3030, 0x3030}, {0x303D, 0x303D}, {0x3297, 0x3297}, + {0x3299, 0x3299}, {0x1F004, 0x1F004}, {0x1F02C, 0x1F02F}, + {0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0}, + {0x1F0CF, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F170, 0x1F171}, + {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, + {0x1F1AE, 0x1F1E5}, {0x1F201, 0x1F20F}, {0x1F21A, 0x1F21A}, + {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, {0x1F23C, 0x1F23F}, + {0x1F249, 0x1F25F}, {0x1F266, 0x1F321}, {0x1F324, 0x1F393}, + {0x1F396, 0x1F397}, {0x1F399, 0x1F39B}, {0x1F39E, 0x1F3F0}, + {0x1F3F3, 0x1F3F5}, {0x1F3F7, 0x1F3FA}, {0x1F400, 0x1F4FD}, + {0x1F4FF, 0x1F53D}, {0x1F549, 0x1F54E}, {0x1F550, 0x1F567}, + {0x1F56F, 0x1F570}, {0x1F573, 0x1F57A}, {0x1F587, 0x1F587}, + {0x1F58A, 0x1F58D}, {0x1F590, 0x1F590}, {0x1F595, 0x1F596}, + {0x1F5A4, 0x1F5A5}, {0x1F5A8, 0x1F5A8}, {0x1F5B1, 0x1F5B2}, + {0x1F5BC, 0x1F5BC}, {0x1F5C2, 0x1F5C4}, {0x1F5D1, 0x1F5D3}, + {0x1F5DC, 0x1F5DE}, {0x1F5E1, 0x1F5E1}, {0x1F5E3, 0x1F5E3}, + {0x1F5E8, 0x1F5E8}, {0x1F5EF, 0x1F5EF}, {0x1F5F3, 0x1F5F3}, + {0x1F5FA, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CB, 0x1F6D2}, + {0x1F6D5, 0x1F6E5}, {0x1F6E9, 0x1F6E9}, {0x1F6EB, 0x1F6F0}, + {0x1F6F3, 0x1F6FF}, {0x1F7DA, 0x1F7FF}, {0x1F80C, 0x1F80F}, + {0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, + {0x1F8AE, 0x1F8AF}, {0x1F8BC, 0x1F8BF}, {0x1F8C2, 0x1F8CF}, + {0x1F8D9, 0x1F8FF}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, + {0x1F947, 0x1F9FF}, {0x1FA58, 0x1FA5F}, {0x1FA6E, 0x1FAFF}, {0x1FC00, 0x1FFFD}, } diff --git a/vendor/github.com/muesli/termenv/.gitignore b/vendor/github.com/muesli/cancelreader/.gitignore similarity index 100% rename from vendor/github.com/muesli/termenv/.gitignore rename to vendor/github.com/muesli/cancelreader/.gitignore diff --git a/vendor/github.com/muesli/termenv/.golangci-soft.yml b/vendor/github.com/muesli/cancelreader/.golangci-soft.yml similarity index 89% rename from vendor/github.com/muesli/termenv/.golangci-soft.yml rename to vendor/github.com/muesli/cancelreader/.golangci-soft.yml index 84e3d41d..ef456e06 100644 --- a/vendor/github.com/muesli/termenv/.golangci-soft.yml +++ b/vendor/github.com/muesli/cancelreader/.golangci-soft.yml @@ -20,9 +20,10 @@ linters: - goconst - godot - godox - - mnd + - gomnd - gomoddirectives - goprintffuncname + - ifshort # - lll - misspell - nakedret @@ -34,10 +35,13 @@ linters: # disable default linters, they are already enabled in .golangci.yml disable: + - deadcode - errcheck - gosimple - govet - ineffassign - staticcheck + - structcheck - typecheck - unused + - varcheck diff --git a/vendor/github.com/muesli/termenv/.golangci.yml b/vendor/github.com/muesli/cancelreader/.golangci.yml similarity index 94% rename from vendor/github.com/muesli/termenv/.golangci.yml rename to vendor/github.com/muesli/cancelreader/.golangci.yml index 684d54bf..a5a91d0d 100644 --- a/vendor/github.com/muesli/termenv/.golangci.yml +++ b/vendor/github.com/muesli/cancelreader/.golangci.yml @@ -15,6 +15,7 @@ issues: linters: enable: - bodyclose + - exportloopref - goimports - gosec - nilerr diff --git a/vendor/github.com/muesli/cancelreader/LICENSE b/vendor/github.com/muesli/cancelreader/LICENSE new file mode 100644 index 00000000..4b19b92d --- /dev/null +++ b/vendor/github.com/muesli/cancelreader/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Erik Geiser and Christian Muehlhaeuser + +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/muesli/cancelreader/README.md b/vendor/github.com/muesli/cancelreader/README.md new file mode 100644 index 00000000..83609fbc --- /dev/null +++ b/vendor/github.com/muesli/cancelreader/README.md @@ -0,0 +1,64 @@ +# CancelReader + +[![Latest Release](https://img.shields.io/github/release/muesli/cancelreader.svg?style=for-the-badge)](https://github.com/muesli/cancelreader/releases) +[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge)](https://pkg.go.dev/github.com/muesli/cancelreader) +[![Software License](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](/LICENSE) +[![Build Status](https://img.shields.io/github/workflow/status/muesli/cancelreader/build?style=for-the-badge)](https://github.com/muesli/cancelreader/actions) +[![Go ReportCard](https://goreportcard.com/badge/github.com/muesli/cancelreader?style=for-the-badge)](https://goreportcard.com/report/muesli/cancelreader) + +A cancelable reader for Go + +This package is based on the fantastic work of [Erik Geiser](https://github.com/erikgeiser) +in Charm's [Bubble Tea](https://github.com/charmbracelet/bubbletea) framework. + +## Usage + +`NewReader` returns a reader with a `Cancel` function. If the input reader is a +`File`, the cancel function can be used to interrupt a blocking `Read` call. +In this case, the cancel function returns true if the call was canceled +successfully. If the input reader is not a `File`, the cancel function does +nothing and always returns false. + +```go +r, err := cancelreader.NewReader(file) +if err != nil { + // handle error + ... +} + +// cancel after five seconds +go func() { + time.Sleep(5 * time.Second) + r.Cancel() +}() + +// keep reading +for { + var buf [1024]byte + _, err := r.Read(buf[:]) + + if errors.Is(err, cancelreader.ErrCanceled) { + fmt.Println("canceled!") + break + } + if err != nil { + // handle other errors + ... + } + + // handle data + ... +} +``` + +## Implementations + +- The Linux implementation is based on the epoll mechanism +- The BSD and macOS implementation is based on the kqueue mechanism +- The generic Unix implementation is based on the posix select syscall + +## Caution + +The Windows implementation is based on WaitForMultipleObject with overlapping +reads from CONIN$. At this point it only supports canceling reads from +`os.Stdin`. diff --git a/vendor/github.com/muesli/cancelreader/cancelreader.go b/vendor/github.com/muesli/cancelreader/cancelreader.go new file mode 100644 index 00000000..18d38255 --- /dev/null +++ b/vendor/github.com/muesli/cancelreader/cancelreader.go @@ -0,0 +1,93 @@ +package cancelreader + +import ( + "fmt" + "io" + "sync" +) + +// ErrCanceled gets returned when trying to read from a canceled reader. +var ErrCanceled = fmt.Errorf("read canceled") + +// CancelReader is a io.Reader whose Read() calls can be canceled without data +// being consumed. The cancelReader has to be closed. +type CancelReader interface { + io.ReadCloser + + // Cancel cancels ongoing and future reads an returns true if it succeeded. + Cancel() bool +} + +// File represents an input/output resource with a file descriptor. +type File interface { + io.ReadWriteCloser + + // Fd returns its file descriptor + Fd() uintptr + + // Name returns its file name. + Name() string +} + +// fallbackCancelReader implements cancelReader but does not actually support +// cancelation during an ongoing Read() call. Thus, Cancel() always returns +// false. However, after calling Cancel(), new Read() calls immediately return +// errCanceled and don't consume any data anymore. +type fallbackCancelReader struct { + r io.Reader + cancelMixin +} + +// newFallbackCancelReader is a fallback for NewReader that cannot actually +// cancel an ongoing read but will immediately return on future reads if it has +// been canceled. +func newFallbackCancelReader(reader io.Reader) (CancelReader, error) { + return &fallbackCancelReader{r: reader}, nil +} + +func (r *fallbackCancelReader) Read(data []byte) (int, error) { + if r.isCanceled() { + return 0, ErrCanceled + } + + n, err := r.r.Read(data) + /* + If the underlying reader is a blocking reader (e.g. an open connection), + it might happen that 1 goroutine cancels the reader while its stuck in + the read call waiting for something. + If that happens, we should still cancel the read. + */ + if r.isCanceled() { + return 0, ErrCanceled + } + return n, err // nolint: wrapcheck +} + +func (r *fallbackCancelReader) Cancel() bool { + r.setCanceled() + return false +} + +func (r *fallbackCancelReader) Close() error { + return nil +} + +// cancelMixin represents a goroutine-safe cancelation status. +type cancelMixin struct { + unsafeCanceled bool + lock sync.Mutex +} + +func (c *cancelMixin) isCanceled() bool { + c.lock.Lock() + defer c.lock.Unlock() + + return c.unsafeCanceled +} + +func (c *cancelMixin) setCanceled() { + c.lock.Lock() + defer c.lock.Unlock() + + c.unsafeCanceled = true +} diff --git a/vendor/github.com/muesli/cancelreader/cancelreader_bsd.go b/vendor/github.com/muesli/cancelreader/cancelreader_bsd.go new file mode 100644 index 00000000..3ddb6cff --- /dev/null +++ b/vendor/github.com/muesli/cancelreader/cancelreader_bsd.go @@ -0,0 +1,146 @@ +//go:build darwin || freebsd || netbsd || openbsd || dragonfly +// +build darwin freebsd netbsd openbsd dragonfly + +package cancelreader + +import ( + "errors" + "fmt" + "io" + "os" + "strings" + + "golang.org/x/sys/unix" +) + +// NewReader returns a reader and a cancel function. If the input reader is a +// File, the cancel function can be used to interrupt a blocking read call. +// In this case, the cancel function returns true if the call was canceled +// successfully. If the input reader is not a File, the cancel function +// does nothing and always returns false. The BSD and macOS implementation is +// based on the kqueue mechanism. +func NewReader(reader io.Reader) (CancelReader, error) { + file, ok := reader.(File) + if !ok { + return newFallbackCancelReader(reader) + } + + // kqueue returns instantly when polling /dev/tty so fallback to select + if file.Name() == "/dev/tty" { + return newSelectCancelReader(reader) + } + + kQueue, err := unix.Kqueue() + if err != nil { + return nil, fmt.Errorf("create kqueue: %w", err) + } + + r := &kqueueCancelReader{ + file: file, + kQueue: kQueue, + } + + r.cancelSignalReader, r.cancelSignalWriter, err = os.Pipe() + if err != nil { + _ = unix.Close(kQueue) + return nil, err + } + + unix.SetKevent(&r.kQueueEvents[0], int(file.Fd()), unix.EVFILT_READ, unix.EV_ADD) + unix.SetKevent(&r.kQueueEvents[1], int(r.cancelSignalReader.Fd()), unix.EVFILT_READ, unix.EV_ADD) + + return r, nil +} + +type kqueueCancelReader struct { + file File + cancelSignalReader File + cancelSignalWriter File + cancelMixin + kQueue int + kQueueEvents [2]unix.Kevent_t +} + +func (r *kqueueCancelReader) Read(data []byte) (int, error) { + if r.isCanceled() { + return 0, ErrCanceled + } + + err := r.wait() + if err != nil { + if errors.Is(err, ErrCanceled) { + // remove signal from pipe + var b [1]byte + _, errRead := r.cancelSignalReader.Read(b[:]) + if errRead != nil { + return 0, fmt.Errorf("reading cancel signal: %w", errRead) + } + } + + return 0, err + } + + return r.file.Read(data) +} + +func (r *kqueueCancelReader) Cancel() bool { + r.setCanceled() + + // send cancel signal + _, err := r.cancelSignalWriter.Write([]byte{'c'}) + return err == nil +} + +func (r *kqueueCancelReader) Close() error { + var errMsgs []string + + // close kqueue + err := unix.Close(r.kQueue) + if err != nil { + errMsgs = append(errMsgs, fmt.Sprintf("closing kqueue: %v", err)) + } + + // close pipe + err = r.cancelSignalWriter.Close() + if err != nil { + errMsgs = append(errMsgs, fmt.Sprintf("closing cancel signal writer: %v", err)) + } + + err = r.cancelSignalReader.Close() + if err != nil { + errMsgs = append(errMsgs, fmt.Sprintf("closing cancel signal reader: %v", err)) + } + + if len(errMsgs) > 0 { + return fmt.Errorf(strings.Join(errMsgs, ", ")) + } + + return nil +} + +func (r *kqueueCancelReader) wait() error { + events := make([]unix.Kevent_t, 1) + + for { + _, err := unix.Kevent(r.kQueue, r.kQueueEvents[:], events, nil) + if errors.Is(err, unix.EINTR) { + continue // try again if the syscall was interrupted + } + + if err != nil { + return fmt.Errorf("kevent: %w", err) + } + + break + } + + ident := uint64(events[0].Ident) + switch ident { + case uint64(r.file.Fd()): + return nil + case uint64(r.cancelSignalReader.Fd()): + return ErrCanceled + } + + return fmt.Errorf("unknown error") +} diff --git a/vendor/github.com/muesli/cancelreader/cancelreader_default.go b/vendor/github.com/muesli/cancelreader/cancelreader_default.go new file mode 100644 index 00000000..8e275fa4 --- /dev/null +++ b/vendor/github.com/muesli/cancelreader/cancelreader_default.go @@ -0,0 +1,12 @@ +//go:build !darwin && !windows && !linux && !solaris && !freebsd && !netbsd && !openbsd && !dragonfly +// +build !darwin,!windows,!linux,!solaris,!freebsd,!netbsd,!openbsd,!dragonfly + +package cancelreader + +import "io" + +// NewReader returns a fallbackCancelReader that satisfies the CancelReader but +// does not actually support cancellation. +func NewReader(reader io.Reader) (CancelReader, error) { + return newFallbackCancelReader(reader) +} diff --git a/vendor/github.com/muesli/cancelreader/cancelreader_linux.go b/vendor/github.com/muesli/cancelreader/cancelreader_linux.go new file mode 100644 index 00000000..09f7369f --- /dev/null +++ b/vendor/github.com/muesli/cancelreader/cancelreader_linux.go @@ -0,0 +1,154 @@ +//go:build linux +// +build linux + +package cancelreader + +import ( + "errors" + "fmt" + "io" + "os" + "strings" + + "golang.org/x/sys/unix" +) + +// NewReader returns a reader and a cancel function. If the input reader is a +// File, the cancel function can be used to interrupt a blocking read call. +// In this case, the cancel function returns true if the call was canceled +// successfully. If the input reader is not a File, the cancel function +// does nothing and always returns false. The Linux implementation is based on +// the epoll mechanism. +func NewReader(reader io.Reader) (CancelReader, error) { + file, ok := reader.(File) + if !ok { + return newFallbackCancelReader(reader) + } + + epoll, err := unix.EpollCreate1(0) + if err != nil { + return nil, fmt.Errorf("create epoll: %w", err) + } + + r := &epollCancelReader{ + file: file, + epoll: epoll, + } + + r.cancelSignalReader, r.cancelSignalWriter, err = os.Pipe() + if err != nil { + _ = unix.Close(epoll) + return nil, err + } + + err = unix.EpollCtl(epoll, unix.EPOLL_CTL_ADD, int(file.Fd()), &unix.EpollEvent{ + Events: unix.EPOLLIN, + Fd: int32(file.Fd()), + }) + if err != nil { + _ = unix.Close(epoll) + return nil, fmt.Errorf("add reader to epoll interest list") + } + + err = unix.EpollCtl(epoll, unix.EPOLL_CTL_ADD, int(r.cancelSignalReader.Fd()), &unix.EpollEvent{ + Events: unix.EPOLLIN, + Fd: int32(r.cancelSignalReader.Fd()), + }) + if err != nil { + _ = unix.Close(epoll) + return nil, fmt.Errorf("add reader to epoll interest list") + } + + return r, nil +} + +type epollCancelReader struct { + file File + cancelSignalReader File + cancelSignalWriter File + cancelMixin + epoll int +} + +func (r *epollCancelReader) Read(data []byte) (int, error) { + if r.isCanceled() { + return 0, ErrCanceled + } + + err := r.wait() + if err != nil { + if errors.Is(err, ErrCanceled) { + // remove signal from pipe + var b [1]byte + _, readErr := r.cancelSignalReader.Read(b[:]) + if readErr != nil { + return 0, fmt.Errorf("reading cancel signal: %w", readErr) + } + } + + return 0, err + } + + return r.file.Read(data) +} + +func (r *epollCancelReader) Cancel() bool { + r.setCanceled() + + // send cancel signal + _, err := r.cancelSignalWriter.Write([]byte{'c'}) + return err == nil +} + +func (r *epollCancelReader) Close() error { + var errMsgs []string + + // close kqueue + err := unix.Close(r.epoll) + if err != nil { + errMsgs = append(errMsgs, fmt.Sprintf("closing epoll: %v", err)) + } + + // close pipe + err = r.cancelSignalWriter.Close() + if err != nil { + errMsgs = append(errMsgs, fmt.Sprintf("closing cancel signal writer: %v", err)) + } + + err = r.cancelSignalReader.Close() + if err != nil { + errMsgs = append(errMsgs, fmt.Sprintf("closing cancel signal reader: %v", err)) + } + + if len(errMsgs) > 0 { + return fmt.Errorf(strings.Join(errMsgs, ", ")) + } + + return nil +} + +func (r *epollCancelReader) wait() error { + events := make([]unix.EpollEvent, 1) + + for { + _, err := unix.EpollWait(r.epoll, events, -1) + if errors.Is(err, unix.EINTR) { + continue // try again if the syscall was interrupted + } + + if err != nil { + return fmt.Errorf("kevent: %w", err) + } + + break + } + + switch events[0].Fd { + case int32(r.file.Fd()): + return nil + case int32(r.cancelSignalReader.Fd()): + return ErrCanceled + } + + return fmt.Errorf("unknown error") +} diff --git a/vendor/github.com/muesli/cancelreader/cancelreader_select.go b/vendor/github.com/muesli/cancelreader/cancelreader_select.go new file mode 100644 index 00000000..03f2a3e1 --- /dev/null +++ b/vendor/github.com/muesli/cancelreader/cancelreader_select.go @@ -0,0 +1,136 @@ +//go:build solaris || darwin || freebsd || netbsd || openbsd || dragonfly +// +build solaris darwin freebsd netbsd openbsd dragonfly + +package cancelreader + +import ( + "errors" + "fmt" + "io" + "os" + "strings" + + "golang.org/x/sys/unix" +) + +// newSelectCancelReader returns a reader and a cancel function. If the input +// reader is a File, the cancel function can be used to interrupt a +// blocking call read call. In this case, the cancel function returns true if +// the call was canceled successfully. If the input reader is not a File or +// the file descriptor is 1024 or larger, the cancel function does nothing and +// always returns false. The generic unix implementation is based on the posix +// select syscall. +func newSelectCancelReader(reader io.Reader) (CancelReader, error) { + file, ok := reader.(File) + if !ok || file.Fd() >= unix.FD_SETSIZE { + return newFallbackCancelReader(reader) + } + r := &selectCancelReader{file: file} + + var err error + + r.cancelSignalReader, r.cancelSignalWriter, err = os.Pipe() + if err != nil { + return nil, err + } + + return r, nil +} + +type selectCancelReader struct { + file File + cancelSignalReader File + cancelSignalWriter File + cancelMixin +} + +func (r *selectCancelReader) Read(data []byte) (int, error) { + if r.isCanceled() { + return 0, ErrCanceled + } + + for { + err := waitForRead(r.file, r.cancelSignalReader) + if err != nil { + if errors.Is(err, unix.EINTR) { + continue // try again if the syscall was interrupted + } + + if errors.Is(err, ErrCanceled) { + // remove signal from pipe + var b [1]byte + _, readErr := r.cancelSignalReader.Read(b[:]) + if readErr != nil { + return 0, fmt.Errorf("reading cancel signal: %w", readErr) + } + } + + return 0, err + } + + return r.file.Read(data) + } +} + +func (r *selectCancelReader) Cancel() bool { + r.setCanceled() + + // send cancel signal + _, err := r.cancelSignalWriter.Write([]byte{'c'}) + return err == nil +} + +func (r *selectCancelReader) Close() error { + var errMsgs []string + + // close pipe + err := r.cancelSignalWriter.Close() + if err != nil { + errMsgs = append(errMsgs, fmt.Sprintf("closing cancel signal writer: %v", err)) + } + + err = r.cancelSignalReader.Close() + if err != nil { + errMsgs = append(errMsgs, fmt.Sprintf("closing cancel signal reader: %v", err)) + } + + if len(errMsgs) > 0 { + return fmt.Errorf(strings.Join(errMsgs, ", ")) + } + + return nil +} + +func waitForRead(reader, abort File) error { + readerFd := int(reader.Fd()) + abortFd := int(abort.Fd()) + + maxFd := readerFd + if abortFd > maxFd { + maxFd = abortFd + } + + // this is a limitation of the select syscall + if maxFd >= unix.FD_SETSIZE { + return fmt.Errorf("cannot select on file descriptor %d which is larger than 1024", maxFd) + } + + fdSet := &unix.FdSet{} + fdSet.Set(int(reader.Fd())) + fdSet.Set(int(abort.Fd())) + + _, err := unix.Select(maxFd+1, fdSet, nil, nil, nil) + if err != nil { + return fmt.Errorf("select: %w", err) + } + + if fdSet.IsSet(abortFd) { + return ErrCanceled + } + + if fdSet.IsSet(readerFd) { + return nil + } + + return fmt.Errorf("select returned without setting a file descriptor") +} diff --git a/vendor/github.com/muesli/cancelreader/cancelreader_unix.go b/vendor/github.com/muesli/cancelreader/cancelreader_unix.go new file mode 100644 index 00000000..3c10ee09 --- /dev/null +++ b/vendor/github.com/muesli/cancelreader/cancelreader_unix.go @@ -0,0 +1,18 @@ +//go:build solaris +// +build solaris + +package cancelreader + +import ( + "io" +) + +// NewReader returns a reader and a cancel function. If the input reader is a +// File, the cancel function can be used to interrupt a blocking read call. +// In this case, the cancel function returns true if the call was canceled +// successfully. If the input reader is not a File or the file descriptor +// is 1024 or larger, the cancel function does nothing and always returns false. +// The generic unix implementation is based on the posix select syscall. +func NewReader(reader io.Reader) (CancelReader, error) { + return newSelectCancelReader(reader) +} diff --git a/vendor/github.com/muesli/cancelreader/cancelreader_windows.go b/vendor/github.com/muesli/cancelreader/cancelreader_windows.go new file mode 100644 index 00000000..c1dc8d50 --- /dev/null +++ b/vendor/github.com/muesli/cancelreader/cancelreader_windows.go @@ -0,0 +1,244 @@ +//go:build windows +// +build windows + +package cancelreader + +import ( + "fmt" + "io" + "os" + "syscall" + "time" + "unicode/utf16" + + "golang.org/x/sys/windows" +) + +var fileShareValidFlags uint32 = 0x00000007 + +// NewReader returns a reader and a cancel function. If the input reader is a +// File with the same file descriptor as os.Stdin, the cancel function can +// be used to interrupt a blocking read call. In this case, the cancel function +// returns true if the call was canceled successfully. If the input reader is +// not a File with the same file descriptor as os.Stdin, the cancel +// function does nothing and always returns false. The Windows implementation +// is based on WaitForMultipleObject with overlapping reads from CONIN$. +func NewReader(reader io.Reader) (CancelReader, error) { + if f, ok := reader.(File); !ok || f.Fd() != os.Stdin.Fd() { + return newFallbackCancelReader(reader) + } + + // it is necessary to open CONIN$ (NOT windows.STD_INPUT_HANDLE) in + // overlapped mode to be able to use it with WaitForMultipleObjects. + conin, err := windows.CreateFile( + &(utf16.Encode([]rune("CONIN$\x00"))[0]), windows.GENERIC_READ|windows.GENERIC_WRITE, + fileShareValidFlags, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_OVERLAPPED, 0) + if err != nil { + return nil, fmt.Errorf("open CONIN$ in overlapping mode: %w", err) + } + + resetConsole, err := prepareConsole(conin) + if err != nil { + return nil, fmt.Errorf("prepare console: %w", err) + } + + // flush input, otherwise it can contain events which trigger + // WaitForMultipleObjects but which ReadFile cannot read, resulting in an + // un-cancelable read + err = flushConsoleInputBuffer(conin) + if err != nil { + return nil, fmt.Errorf("flush console input buffer: %w", err) + } + + cancelEvent, err := windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + return nil, fmt.Errorf("create stop event: %w", err) + } + + return &winCancelReader{ + conin: conin, + cancelEvent: cancelEvent, + resetConsole: resetConsole, + blockingReadSignal: make(chan struct{}, 1), + }, nil +} + +type winCancelReader struct { + conin windows.Handle + cancelEvent windows.Handle + cancelMixin + + resetConsole func() error + blockingReadSignal chan struct{} +} + +func (r *winCancelReader) Read(data []byte) (int, error) { + if r.isCanceled() { + return 0, ErrCanceled + } + + err := r.wait() + if err != nil { + return 0, err + } + + if r.isCanceled() { + return 0, ErrCanceled + } + + // windows.Read does not work on overlapping windows.Handles + return r.readAsync(data) +} + +// Cancel cancels ongoing and future Read() calls and returns true if the +// cancelation of the ongoing Read() was successful. On Windows Terminal, +// WaitForMultipleObjects sometimes immediately returns without input being +// available. In this case, graceful cancelation is not possible and Cancel() +// returns false. +func (r *winCancelReader) Cancel() bool { + r.setCanceled() + + select { + case r.blockingReadSignal <- struct{}{}: + err := windows.SetEvent(r.cancelEvent) + if err != nil { + return false + } + <-r.blockingReadSignal + case <-time.After(100 * time.Millisecond): + // Read() hangs in a GetOverlappedResult which is likely due to + // WaitForMultipleObjects returning without input being available + // so we cannot cancel this ongoing read. + return false + } + + return true +} + +func (r *winCancelReader) Close() error { + err := windows.CloseHandle(r.cancelEvent) + if err != nil { + return fmt.Errorf("closing cancel event handle: %w", err) + } + + err = r.resetConsole() + if err != nil { + return err + } + + err = windows.Close(r.conin) + if err != nil { + return fmt.Errorf("closing CONIN$") + } + + return nil +} + +func (r *winCancelReader) wait() error { + event, err := windows.WaitForMultipleObjects([]windows.Handle{r.conin, r.cancelEvent}, false, windows.INFINITE) + switch { + case windows.WAIT_OBJECT_0 <= event && event < windows.WAIT_OBJECT_0+2: + if event == windows.WAIT_OBJECT_0+1 { + return ErrCanceled + } + + if event == windows.WAIT_OBJECT_0 { + return nil + } + + return fmt.Errorf("unexpected wait object is ready: %d", event-windows.WAIT_OBJECT_0) + case windows.WAIT_ABANDONED <= event && event < windows.WAIT_ABANDONED+2: + return fmt.Errorf("abandoned") + case event == uint32(windows.WAIT_TIMEOUT): + return fmt.Errorf("timeout") + case event == windows.WAIT_FAILED: + return fmt.Errorf("failed") + default: + return fmt.Errorf("unexpected error: %w", error(err)) + } +} + +// readAsync is necessary to read from a windows.Handle in overlapping mode. +func (r *winCancelReader) readAsync(data []byte) (int, error) { + hevent, err := windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + return 0, fmt.Errorf("create event: %w", err) + } + + overlapped := windows.Overlapped{ + HEvent: hevent, + } + + var n uint32 + + err = windows.ReadFile(r.conin, data, &n, &overlapped) + if err != nil && err != windows.ERROR_IO_PENDING { + return int(n), err + } + + r.blockingReadSignal <- struct{}{} + err = windows.GetOverlappedResult(r.conin, &overlapped, &n, true) + if err != nil { + return int(n), nil + } + <-r.blockingReadSignal + + return int(n), nil +} + +func prepareConsole(input windows.Handle) (reset func() error, err error) { + var originalMode uint32 + + err = windows.GetConsoleMode(input, &originalMode) + if err != nil { + return nil, fmt.Errorf("get console mode: %w", err) + } + + var newMode uint32 + newMode &^= windows.ENABLE_ECHO_INPUT + newMode &^= windows.ENABLE_LINE_INPUT + newMode &^= windows.ENABLE_MOUSE_INPUT + newMode &^= windows.ENABLE_WINDOW_INPUT + newMode &^= windows.ENABLE_PROCESSED_INPUT + + newMode |= windows.ENABLE_EXTENDED_FLAGS + newMode |= windows.ENABLE_INSERT_MODE + newMode |= windows.ENABLE_QUICK_EDIT_MODE + + // Enabling virtual terminal input is necessary for processing certain + // types of input like X10 mouse events and arrows keys with the current + // bytes-based input reader. It does, however, prevent cancelReader from + // being able to cancel input. The planned solution for this is to read + // Windows events in a more native fashion, rather than the current simple + // bytes-based input reader which works well on unix systems. + newMode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT + + err = windows.SetConsoleMode(input, newMode) + if err != nil { + return nil, fmt.Errorf("set console mode: %w", err) + } + + return func() error { + err := windows.SetConsoleMode(input, originalMode) + if err != nil { + return fmt.Errorf("reset console mode: %w", err) + } + + return nil + }, nil +} + +var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + procFlushConsoleInputBuffer = modkernel32.NewProc("FlushConsoleInputBuffer") +) + +func flushConsoleInputBuffer(consoleInput windows.Handle) error { + r, _, e := syscall.Syscall(procFlushConsoleInputBuffer.Addr(), 1, + uintptr(consoleInput), 0, 0) + if r == 0 { + return error(e) + } + + return nil +} diff --git a/vendor/github.com/muesli/termenv/README.md b/vendor/github.com/muesli/termenv/README.md deleted file mode 100644 index fa7929d4..00000000 --- a/vendor/github.com/muesli/termenv/README.md +++ /dev/null @@ -1,431 +0,0 @@ -

- termenv Logo -
- Latest Release - GoDoc - Build Status - Coverage Status - Go ReportCard -
- Example terminal output -

- -`termenv` lets you safely use advanced styling options on the terminal. It -gathers information about the terminal environment in terms of its ANSI & color -support and offers you convenient methods to colorize and style your output, -without you having to deal with all kinds of weird ANSI escape sequences and -color conversions. - -## Features - -- RGB/TrueColor support -- Detects the supported color range of your terminal -- Automatically converts colors to the best matching, available colors -- Terminal theme (light/dark) detection -- Chainable syntax -- Nested styles - -## Installation - -```bash -go get github.com/muesli/termenv -``` - -## Usage - -```go -output := termenv.NewOutput(os.Stdout) -``` - -`termenv` queries the terminal's capabilities it is running in, so you can -safely use advanced features, like RGB colors or ANSI styles. `output.Profile` -returns the supported profile: - -- `termenv.Ascii` - no ANSI support detected, ASCII only -- `termenv.ANSI` - 16 color ANSI support -- `termenv.ANSI256` - Extended 256 color ANSI support -- `termenv.TrueColor` - RGB/TrueColor support - -Alternatively, you can use `termenv.EnvColorProfile` which evaluates the -terminal like `ColorProfile`, but also respects the `NO_COLOR` and -`CLICOLOR_FORCE` environment variables. - -You can also query the terminal for its color scheme, so you know whether your -app is running in a light- or dark-themed environment: - -```go -// Returns terminal's foreground color -color := output.ForegroundColor() - -// Returns terminal's background color -color := output.BackgroundColor() - -// Returns whether terminal uses a dark-ish background -darkTheme := output.HasDarkBackground() -``` - -### Manual Profile Selection - -If you don't want to rely on the automatic detection, you can manually select -the profile you want to use: - -```go -output := termenv.NewOutput(os.Stdout, termenv.WithProfile(termenv.TrueColor)) -``` - -## Colors - -`termenv` supports multiple color profiles: Ascii (black & white only), -ANSI (16 colors), ANSI Extended (256 colors), and TrueColor (24-bit RGB). Colors -will automatically be degraded to the best matching available color in the -desired profile: - -`TrueColor` => `ANSI 256 Colors` => `ANSI 16 Colors` => `Ascii` - -```go -s := output.String("Hello World") - -// Supports hex values -// Will automatically degrade colors on terminals not supporting RGB -s.Foreground(output.Color("#abcdef")) -// but also supports ANSI colors (0-255) -s.Background(output.Color("69")) -// ...or the color.Color interface -s.Foreground(output.FromColor(color.RGBA{255, 128, 0, 255})) - -// Combine fore- & background colors -s.Foreground(output.Color("#ffffff")).Background(output.Color("#0000ff")) - -// Supports the fmt.Stringer interface -fmt.Println(s) -``` - -## Styles - -You can use a chainable syntax to compose your own styles: - -```go -s := output.String("foobar") - -// Text styles -s.Bold() -s.Faint() -s.Italic() -s.CrossOut() -s.Underline() -s.Overline() - -// Reverse swaps current fore- & background colors -s.Reverse() - -// Blinking text -s.Blink() - -// Combine multiple options -s.Bold().Underline() -``` - -## Template Helpers - -`termenv` provides a set of helper functions to style your Go templates: - -```go -// load template helpers -f := output.TemplateFuncs() -tpl := template.New("tpl").Funcs(f) - -// apply bold style in a template -bold := `{{ Bold "Hello World" }}` - -// examples for colorized templates -col := `{{ Color "#ff0000" "#0000ff" "Red on Blue" }}` -fg := `{{ Foreground "#ff0000" "Red Foreground" }}` -bg := `{{ Background "#0000ff" "Blue Background" }}` - -// wrap styles -wrap := `{{ Bold (Underline "Hello World") }}` - -// parse and render -tpl, err = tpl.Parse(bold) - -var buf bytes.Buffer -tpl.Execute(&buf, nil) -fmt.Println(&buf) -``` - -Other available helper functions are: `Faint`, `Italic`, `CrossOut`, -`Underline`, `Overline`, `Reverse`, and `Blink`. - -## Positioning - -```go -// Move the cursor to a given position -output.MoveCursor(row, column) - -// Save the cursor position -output.SaveCursorPosition() - -// Restore a saved cursor position -output.RestoreCursorPosition() - -// Move the cursor up a given number of lines -output.CursorUp(n) - -// Move the cursor down a given number of lines -output.CursorDown(n) - -// Move the cursor up a given number of lines -output.CursorForward(n) - -// Move the cursor backwards a given number of cells -output.CursorBack(n) - -// Move the cursor down a given number of lines and place it at the beginning -// of the line -output.CursorNextLine(n) - -// Move the cursor up a given number of lines and place it at the beginning of -// the line -output.CursorPrevLine(n) -``` - -## Screen - -```go -// Reset the terminal to its default style, removing any active styles -output.Reset() - -// RestoreScreen restores a previously saved screen state -output.RestoreScreen() - -// SaveScreen saves the screen state -output.SaveScreen() - -// Switch to the altscreen. The former view can be restored with ExitAltScreen() -output.AltScreen() - -// Exit the altscreen and return to the former terminal view -output.ExitAltScreen() - -// Clear the visible portion of the terminal -output.ClearScreen() - -// Clear the current line -output.ClearLine() - -// Clear a given number of lines -output.ClearLines(n) - -// Set the scrolling region of the terminal -output.ChangeScrollingRegion(top, bottom) - -// Insert the given number of lines at the top of the scrollable region, pushing -// lines below down -output.InsertLines(n) - -// Delete the given number of lines, pulling any lines in the scrollable region -// below up -output.DeleteLines(n) -``` - -## Session - -```go -// SetWindowTitle sets the terminal window title -output.SetWindowTitle(title) - -// SetForegroundColor sets the default foreground color -output.SetForegroundColor(color) - -// SetBackgroundColor sets the default background color -output.SetBackgroundColor(color) - -// SetCursorColor sets the cursor color -output.SetCursorColor(color) - -// Hide the cursor -output.HideCursor() - -// Show the cursor -output.ShowCursor() - -// Copy to clipboard -output.Copy(message) - -// Copy to primary clipboard (X11) -output.CopyPrimary(message) - -// Trigger notification -output.Notify(title, body) -``` - -## Mouse - -```go -// Enable X10 mouse mode, only button press events are sent -output.EnableMousePress() - -// Disable X10 mouse mode -output.DisableMousePress() - -// Enable Mouse Tracking mode -output.EnableMouse() - -// Disable Mouse Tracking mode -output.DisableMouse() - -// Enable Hilite Mouse Tracking mode -output.EnableMouseHilite() - -// Disable Hilite Mouse Tracking mode -output.DisableMouseHilite() - -// Enable Cell Motion Mouse Tracking mode -output.EnableMouseCellMotion() - -// Disable Cell Motion Mouse Tracking mode -output.DisableMouseCellMotion() - -// Enable All Motion Mouse mode -output.EnableMouseAllMotion() - -// Disable All Motion Mouse mode -output.DisableMouseAllMotion() -``` - -## Bracketed Paste - -```go -// Enables bracketed paste mode -termenv.EnableBracketedPaste() - -// Disables bracketed paste mode -termenv.DisableBracketedPaste() -``` - -## Terminal Feature Support - -### Color Support - -- 24-bit (RGB): alacritty, foot, iTerm, kitty, Konsole, st, tmux, vte-based, wezterm, Ghostty, Windows Terminal -- 8-bit (256): rxvt, screen, xterm, Apple Terminal -- 4-bit (16): Linux Console - -### Control Sequences - -
-Click to show feature matrix - -| Terminal | Query Color Scheme | Query Cursor Position | Set Window Title | Change Cursor Color | Change Default Foreground Setting | Change Default Background Setting | Bracketed Paste | Extended Mouse (SGR) | Pixels Mouse (SGR-Pixels) | -| ---------------- | :----------------: | :-------------------: | :--------------: | :-----------------: | :-------------------------------: | :-------------------------------: | :-------------: | :------------------: | :-----------------------: | -| alacritty | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| foot | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| kitty | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| Konsole | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | -| rxvt | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | -| urxvt | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| screen | ⛔[^mux] | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | -| st | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| tmux | ⛔[^mux] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| vte-based[^vte] | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | -| wezterm | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| xterm | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | -| Linux Console | ❌ | ✅ | ⛔ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| Apple Terminal | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | -| iTerm | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | -| Windows cmd | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | -| Windows Terminal | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | - -[^vte]: This covers all vte-based terminals, including Gnome Terminal, guake, Pantheon Terminal, Terminator, Tilix, XFCE Terminal. -[^mux]: Unavailable as multiplexers (like tmux or screen) can be connected to multiple terminals (with different color settings) at the same time. - -You can help improve this list! Check out [how to](ansi_compat.md) and open an issue or pull request. - -
- -### System Commands - -
-Click to show feature matrix - -| Terminal | Copy to Clipboard (OSC52) | Hyperlinks (OSC8) | Notifications (OSC777) | -| ---------------- | :-----------------------: | :---------------: | :--------------------: | -| alacritty | ✅ | ✅[^alacritty] | ❌ | -| foot | ✅ | ✅ | ✅ | -| kitty | ✅ | ✅ | ✅ | -| Konsole | ❌[^konsole] | ✅ | ❌ | -| rxvt | ❌ | ❌ | ❌ | -| urxvt | ✅[^urxvt] | ❌ | ✅ | -| screen | ✅ | ❌[^screen] | ❌ | -| st | ✅ | ❌ | ❌ | -| tmux | ✅ | ❌[^tmux] | ❌ | -| vte-based[^vte] | ❌[^vte] | ✅ | ❌ | -| wezterm | ✅ | ✅ | ❌ | -| xterm | ✅ | ❌ | ❌ | -| Linux Console | ⛔ | ⛔ | ❌ | -| Apple Terminal | ✅[^apple] | ❌ | ❌ | -| iTerm | ✅ | ✅ | ❌ | -| Windows cmd | ❌ | ❌ | ❌ | -| Windows Terminal | ✅ | ✅ | ❌ | - -[^vte]: This covers all vte-based terminals, including Gnome Terminal, guake, Pantheon Terminal, Terminator, Tilix, XFCE Terminal. OSC52 is not supported, see [issue#2495](https://gitlab.gnome.org/GNOME/vte/-/issues/2495). -[^urxvt]: Workaround for urxvt not supporting OSC52. See [this](https://unix.stackexchange.com/a/629485) for more information. -[^konsole]: OSC52 is not supported, for more info see [bug#372116](https://bugs.kde.org/show_bug.cgi?id=372116). -[^apple]: OSC52 works with a [workaround](https://github.com/roy2220/osc52pty). -[^tmux]: OSC8 is not supported, for more info see [issue#911](https://github.com/tmux/tmux/issues/911). -[^screen]: OSC8 is not supported, for more info see [bug#50952](https://savannah.gnu.org/bugs/index.php?50952). -[^alacritty]: OSC8 is supported since [v0.11.0](https://github.com/alacritty/alacritty/releases/tag/v0.11.0) - -
- -## Platform Support - -`termenv` works on Unix systems (like Linux, macOS, or BSD) and Windows. While -terminal applications on Unix support ANSI styling out-of-the-box, on Windows -you need to enable ANSI processing in your application first: - -```go - restoreConsole, err := termenv.EnableVirtualTerminalProcessing(termenv.DefaultOutput()) - if err != nil { - panic(err) - } - defer restoreConsole() -``` - -The above code is safe to include on non-Windows systems or when os.Stdout does -not refer to a terminal (e.g. in tests). - -## Color Chart - -![ANSI color chart](https://github.com/muesli/termenv/raw/master/examples/color-chart/color-chart.png) - -You can find the source code used to create this chart in `termenv`'s examples. - -## Related Projects - -- [reflow](https://github.com/muesli/reflow) - ANSI-aware text operations -- [Lip Gloss](https://github.com/charmbracelet/lipgloss) - style definitions for nice terminal layouts 👄 -- [ansi](https://github.com/muesli/ansi) - ANSI sequence helpers - -## termenv in the Wild - -Need some inspiration or just want to see how others are using `termenv`? Check -out these projects: - -- [Bubble Tea](https://github.com/charmbracelet/bubbletea) - a powerful little TUI framework 🏗 -- [Glamour](https://github.com/charmbracelet/glamour) - stylesheet-based markdown rendering for your CLI apps 💇🏻‍♀️ -- [Glow](https://github.com/charmbracelet/glow) - a markdown renderer for the command-line 💅🏻 -- [duf](https://github.com/muesli/duf) - Disk Usage/Free Utility - a better 'df' alternative -- [gitty](https://github.com/muesli/gitty) - contextual information about your git projects -- [slides](https://github.com/maaslalani/slides) - terminal-based presentation tool - -## Feedback - -Got some feedback or suggestions? Please open an issue or drop me a note! - -- [Twitter](https://twitter.com/mueslix) -- [The Fediverse](https://mastodon.social/@fribbledom) - -## License - -[MIT](https://github.com/muesli/termenv/raw/master/LICENSE) diff --git a/vendor/github.com/muesli/termenv/ansi_compat.md b/vendor/github.com/muesli/termenv/ansi_compat.md deleted file mode 100644 index 6b68a3a9..00000000 --- a/vendor/github.com/muesli/termenv/ansi_compat.md +++ /dev/null @@ -1,65 +0,0 @@ -## Change Foreground Color - -This command should enable a blue foreground color: - -```bash -echo -ne "\033]10;#0000ff\007" -``` - -## Change Background Color - -This command should enable a green background color: - -```bash -echo -ne "\033]11;#00ff00\007" -``` - -## Change Cursor Color - -This command should enable a red cursor color: - -```bash -echo -ne "\033]12;#ff0000\007" -``` - -## Query Color Scheme - -These two commands should print out the currently active color scheme: - -```bash -echo -ne "\033]10;?\033\\" -echo -ne "\033]11;?\033\\" -``` - -## Query Cursor Position - -This command should print out the current cursor position: - -```bash -echo -ne "\033[6n" -``` - -## Set Window Title - -This command should set the window title to "Test": - -```bash -echo -ne "\033]2;Test\007" && sleep 10 -``` - -## Bracketed paste - -Enter this command, then paste a word from the clipboard. The text -displayed on the terminal should contain the codes `200~` and `201~`: - -```bash -echo -ne "\033[?2004h" && sleep 10 -``` - -## Trigger Notification - -This command should trigger a notification: - -```bash -echo -ne "\033]777;notify;Title;Body\033\\" -``` diff --git a/vendor/github.com/muesli/termenv/ansicolors.go b/vendor/github.com/muesli/termenv/ansicolors.go deleted file mode 100644 index 1a301b0f..00000000 --- a/vendor/github.com/muesli/termenv/ansicolors.go +++ /dev/null @@ -1,281 +0,0 @@ -package termenv - -// ANSI color codes. -const ( - ANSIBlack ANSIColor = iota - ANSIRed - ANSIGreen - ANSIYellow - ANSIBlue - ANSIMagenta - ANSICyan - ANSIWhite - ANSIBrightBlack - ANSIBrightRed - ANSIBrightGreen - ANSIBrightYellow - ANSIBrightBlue - ANSIBrightMagenta - ANSIBrightCyan - ANSIBrightWhite -) - -// RGB values of ANSI colors (0-255). -var ansiHex = []string{ - "#000000", - "#800000", - "#008000", - "#808000", - "#000080", - "#800080", - "#008080", - "#c0c0c0", - "#808080", - "#ff0000", - "#00ff00", - "#ffff00", - "#0000ff", - "#ff00ff", - "#00ffff", - "#ffffff", - "#000000", - "#00005f", - "#000087", - "#0000af", - "#0000d7", - "#0000ff", - "#005f00", - "#005f5f", - "#005f87", - "#005faf", - "#005fd7", - "#005fff", - "#008700", - "#00875f", - "#008787", - "#0087af", - "#0087d7", - "#0087ff", - "#00af00", - "#00af5f", - "#00af87", - "#00afaf", - "#00afd7", - "#00afff", - "#00d700", - "#00d75f", - "#00d787", - "#00d7af", - "#00d7d7", - "#00d7ff", - "#00ff00", - "#00ff5f", - "#00ff87", - "#00ffaf", - "#00ffd7", - "#00ffff", - "#5f0000", - "#5f005f", - "#5f0087", - "#5f00af", - "#5f00d7", - "#5f00ff", - "#5f5f00", - "#5f5f5f", - "#5f5f87", - "#5f5faf", - "#5f5fd7", - "#5f5fff", - "#5f8700", - "#5f875f", - "#5f8787", - "#5f87af", - "#5f87d7", - "#5f87ff", - "#5faf00", - "#5faf5f", - "#5faf87", - "#5fafaf", - "#5fafd7", - "#5fafff", - "#5fd700", - "#5fd75f", - "#5fd787", - "#5fd7af", - "#5fd7d7", - "#5fd7ff", - "#5fff00", - "#5fff5f", - "#5fff87", - "#5fffaf", - "#5fffd7", - "#5fffff", - "#870000", - "#87005f", - "#870087", - "#8700af", - "#8700d7", - "#8700ff", - "#875f00", - "#875f5f", - "#875f87", - "#875faf", - "#875fd7", - "#875fff", - "#878700", - "#87875f", - "#878787", - "#8787af", - "#8787d7", - "#8787ff", - "#87af00", - "#87af5f", - "#87af87", - "#87afaf", - "#87afd7", - "#87afff", - "#87d700", - "#87d75f", - "#87d787", - "#87d7af", - "#87d7d7", - "#87d7ff", - "#87ff00", - "#87ff5f", - "#87ff87", - "#87ffaf", - "#87ffd7", - "#87ffff", - "#af0000", - "#af005f", - "#af0087", - "#af00af", - "#af00d7", - "#af00ff", - "#af5f00", - "#af5f5f", - "#af5f87", - "#af5faf", - "#af5fd7", - "#af5fff", - "#af8700", - "#af875f", - "#af8787", - "#af87af", - "#af87d7", - "#af87ff", - "#afaf00", - "#afaf5f", - "#afaf87", - "#afafaf", - "#afafd7", - "#afafff", - "#afd700", - "#afd75f", - "#afd787", - "#afd7af", - "#afd7d7", - "#afd7ff", - "#afff00", - "#afff5f", - "#afff87", - "#afffaf", - "#afffd7", - "#afffff", - "#d70000", - "#d7005f", - "#d70087", - "#d700af", - "#d700d7", - "#d700ff", - "#d75f00", - "#d75f5f", - "#d75f87", - "#d75faf", - "#d75fd7", - "#d75fff", - "#d78700", - "#d7875f", - "#d78787", - "#d787af", - "#d787d7", - "#d787ff", - "#d7af00", - "#d7af5f", - "#d7af87", - "#d7afaf", - "#d7afd7", - "#d7afff", - "#d7d700", - "#d7d75f", - "#d7d787", - "#d7d7af", - "#d7d7d7", - "#d7d7ff", - "#d7ff00", - "#d7ff5f", - "#d7ff87", - "#d7ffaf", - "#d7ffd7", - "#d7ffff", - "#ff0000", - "#ff005f", - "#ff0087", - "#ff00af", - "#ff00d7", - "#ff00ff", - "#ff5f00", - "#ff5f5f", - "#ff5f87", - "#ff5faf", - "#ff5fd7", - "#ff5fff", - "#ff8700", - "#ff875f", - "#ff8787", - "#ff87af", - "#ff87d7", - "#ff87ff", - "#ffaf00", - "#ffaf5f", - "#ffaf87", - "#ffafaf", - "#ffafd7", - "#ffafff", - "#ffd700", - "#ffd75f", - "#ffd787", - "#ffd7af", - "#ffd7d7", - "#ffd7ff", - "#ffff00", - "#ffff5f", - "#ffff87", - "#ffffaf", - "#ffffd7", - "#ffffff", - "#080808", - "#121212", - "#1c1c1c", - "#262626", - "#303030", - "#3a3a3a", - "#444444", - "#4e4e4e", - "#585858", - "#626262", - "#6c6c6c", - "#767676", - "#808080", - "#8a8a8a", - "#949494", - "#9e9e9e", - "#a8a8a8", - "#b2b2b2", - "#bcbcbc", - "#c6c6c6", - "#d0d0d0", - "#dadada", - "#e4e4e4", - "#eeeeee", -} diff --git a/vendor/github.com/muesli/termenv/color.go b/vendor/github.com/muesli/termenv/color.go deleted file mode 100644 index 59e639b1..00000000 --- a/vendor/github.com/muesli/termenv/color.go +++ /dev/null @@ -1,205 +0,0 @@ -package termenv - -import ( - "errors" - "fmt" - "math" - "strings" - - "github.com/lucasb-eyer/go-colorful" -) - -// ErrInvalidColor gets returned when a color is invalid. -var ErrInvalidColor = errors.New("invalid color") - -// Foreground and Background sequence codes. -const ( - Foreground = "38" - Background = "48" -) - -// Color is an interface implemented by all colors that can be converted to an -// ANSI sequence. -type Color interface { - // Sequence returns the ANSI Sequence for the color. - Sequence(bg bool) string -} - -// NoColor is a nop for terminals that don't support colors. -type NoColor struct{} - -func (c NoColor) String() string { - return "" -} - -// ANSIColor is a color (0-15) as defined by the ANSI Standard. -type ANSIColor int - -func (c ANSIColor) String() string { - return ansiHex[c] -} - -// ANSI256Color is a color (16-255) as defined by the ANSI Standard. -type ANSI256Color int - -func (c ANSI256Color) String() string { - return ansiHex[c] -} - -// RGBColor is a hex-encoded color, e.g. "#abcdef". -type RGBColor string - -// ConvertToRGB converts a Color to a colorful.Color. -func ConvertToRGB(c Color) colorful.Color { - var hex string - switch v := c.(type) { - case RGBColor: - hex = string(v) - case ANSIColor: - hex = ansiHex[v] - case ANSI256Color: - hex = ansiHex[v] - } - - ch, _ := colorful.Hex(hex) - return ch -} - -// Sequence returns the ANSI Sequence for the color. -func (c NoColor) Sequence(_ bool) string { - return "" -} - -// Sequence returns the ANSI Sequence for the color. -// -//nolint:mnd -func (c ANSIColor) Sequence(bg bool) string { - col := int(c) - bgMod := func(c int) int { - if bg { - return c + 10 - } - return c - } - - if col < 8 { - return fmt.Sprintf("%d", bgMod(col)+30) //nolint:mnd - } - return fmt.Sprintf("%d", bgMod(col-8)+90) //nolint:mnd -} - -// Sequence returns the ANSI Sequence for the color. -func (c ANSI256Color) Sequence(bg bool) string { - prefix := Foreground - if bg { - prefix = Background - } - return fmt.Sprintf("%s;5;%d", prefix, c) -} - -// Sequence returns the ANSI Sequence for the color. -func (c RGBColor) Sequence(bg bool) string { - f, err := colorful.Hex(string(c)) - if err != nil { - return "" - } - - prefix := Foreground - if bg { - prefix = Background - } - return fmt.Sprintf("%s;2;%d;%d;%d", prefix, uint8(f.R*255), uint8(f.G*255), uint8(f.B*255)) //nolint:mnd -} - -func xTermColor(s string) (RGBColor, error) { - if len(s) < 24 || len(s) > 25 { - return RGBColor(""), ErrInvalidColor - } - - switch { - case strings.HasSuffix(s, string(BEL)): - s = strings.TrimSuffix(s, string(BEL)) - case strings.HasSuffix(s, string(ESC)): - s = strings.TrimSuffix(s, string(ESC)) - case strings.HasSuffix(s, ST): - s = strings.TrimSuffix(s, ST) - default: - return RGBColor(""), ErrInvalidColor - } - - s = s[4:] - - prefix := ";rgb:" - if !strings.HasPrefix(s, prefix) { - return RGBColor(""), ErrInvalidColor - } - s = strings.TrimPrefix(s, prefix) - - h := strings.Split(s, "/") - hex := fmt.Sprintf("#%s%s%s", h[0][:2], h[1][:2], h[2][:2]) - return RGBColor(hex), nil -} - -func ansi256ToANSIColor(c ANSI256Color) ANSIColor { - var r int - md := math.MaxFloat64 - - h, _ := colorful.Hex(ansiHex[c]) - for i := 0; i <= 15; i++ { - hb, _ := colorful.Hex(ansiHex[i]) - d := h.DistanceHSLuv(hb) - - if d < md { - md = d - r = i - } - } - - return ANSIColor(r) -} - -//nolint:mnd -func hexToANSI256Color(c colorful.Color) ANSI256Color { - v2ci := func(v float64) int { - if v < 48 { - return 0 - } - if v < 115 { - return 1 - } - return int((v - 35) / 40) - } - - // Calculate the nearest 0-based color index at 16..231 - r := v2ci(c.R * 255.0) // 0..5 each - g := v2ci(c.G * 255.0) - b := v2ci(c.B * 255.0) - ci := 36*r + 6*g + b /* 0..215 */ - - // Calculate the represented colors back from the index - i2cv := [6]int{0, 0x5f, 0x87, 0xaf, 0xd7, 0xff} - cr := i2cv[r] // r/g/b, 0..255 each - cg := i2cv[g] - cb := i2cv[b] - - // Calculate the nearest 0-based gray index at 232..255 - var grayIdx int - average := (r + g + b) / 3 - if average > 238 { - grayIdx = 23 - } else { - grayIdx = (average - 3) / 10 // 0..23 - } - gv := 8 + 10*grayIdx // same value for r/g/b, 0..255 - - // Return the one which is nearer to the original input rgb value - c2 := colorful.Color{R: float64(cr) / 255.0, G: float64(cg) / 255.0, B: float64(cb) / 255.0} - g2 := colorful.Color{R: float64(gv) / 255.0, G: float64(gv) / 255.0, B: float64(gv) / 255.0} - colorDist := c.DistanceHSLuv(c2) - grayDist := c.DistanceHSLuv(g2) - - if colorDist <= grayDist { - return ANSI256Color(16 + ci) - } - return ANSI256Color(232 + grayIdx) -} diff --git a/vendor/github.com/muesli/termenv/constants_linux.go b/vendor/github.com/muesli/termenv/constants_linux.go deleted file mode 100644 index 4262f03b..00000000 --- a/vendor/github.com/muesli/termenv/constants_linux.go +++ /dev/null @@ -1,8 +0,0 @@ -package termenv - -import "golang.org/x/sys/unix" - -const ( - tcgetattr = unix.TCGETS - tcsetattr = unix.TCSETS -) diff --git a/vendor/github.com/muesli/termenv/constants_solaris.go b/vendor/github.com/muesli/termenv/constants_solaris.go deleted file mode 100644 index 4262f03b..00000000 --- a/vendor/github.com/muesli/termenv/constants_solaris.go +++ /dev/null @@ -1,8 +0,0 @@ -package termenv - -import "golang.org/x/sys/unix" - -const ( - tcgetattr = unix.TCGETS - tcsetattr = unix.TCSETS -) diff --git a/vendor/github.com/muesli/termenv/constants_unix.go b/vendor/github.com/muesli/termenv/constants_unix.go deleted file mode 100644 index 5d664245..00000000 --- a/vendor/github.com/muesli/termenv/constants_unix.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && !solaris && !illumos -// +build darwin dragonfly freebsd netbsd openbsd -// +build !solaris -// +build !illumos - -package termenv - -import "golang.org/x/sys/unix" - -const ( - tcgetattr = unix.TIOCGETA - tcsetattr = unix.TIOCSETA -) diff --git a/vendor/github.com/muesli/termenv/constants_zos.go b/vendor/github.com/muesli/termenv/constants_zos.go deleted file mode 100644 index 4262f03b..00000000 --- a/vendor/github.com/muesli/termenv/constants_zos.go +++ /dev/null @@ -1,8 +0,0 @@ -package termenv - -import "golang.org/x/sys/unix" - -const ( - tcgetattr = unix.TCGETS - tcsetattr = unix.TCSETS -) diff --git a/vendor/github.com/muesli/termenv/copy.go b/vendor/github.com/muesli/termenv/copy.go deleted file mode 100644 index 4bf5c9fe..00000000 --- a/vendor/github.com/muesli/termenv/copy.go +++ /dev/null @@ -1,37 +0,0 @@ -package termenv - -import ( - "strings" - - "github.com/aymanbagabas/go-osc52/v2" -) - -// Copy copies text to clipboard using OSC 52 escape sequence. -func (o Output) Copy(str string) { - s := osc52.New(str) - if strings.HasPrefix(o.environ.Getenv("TERM"), "screen") { - s = s.Screen() - } - _, _ = s.WriteTo(o) -} - -// CopyPrimary copies text to primary clipboard (X11) using OSC 52 escape -// sequence. -func (o Output) CopyPrimary(str string) { - s := osc52.New(str).Primary() - if strings.HasPrefix(o.environ.Getenv("TERM"), "screen") { - s = s.Screen() - } - _, _ = s.WriteTo(o) -} - -// Copy copies text to clipboard using OSC 52 escape sequence. -func Copy(str string) { - output.Copy(str) -} - -// CopyPrimary copies text to primary clipboard (X11) using OSC 52 escape -// sequence. -func CopyPrimary(str string) { - output.CopyPrimary(str) -} diff --git a/vendor/github.com/muesli/termenv/hyperlink.go b/vendor/github.com/muesli/termenv/hyperlink.go deleted file mode 100644 index 97e760a3..00000000 --- a/vendor/github.com/muesli/termenv/hyperlink.go +++ /dev/null @@ -1,11 +0,0 @@ -package termenv - -// Hyperlink creates a hyperlink using OSC8. -func Hyperlink(link, name string) string { - return output.Hyperlink(link, name) -} - -// Hyperlink creates a hyperlink using OSC8. -func (o *Output) Hyperlink(link, name string) string { - return OSC + "8;;" + link + ST + name + OSC + "8;;" + ST -} diff --git a/vendor/github.com/muesli/termenv/notification.go b/vendor/github.com/muesli/termenv/notification.go deleted file mode 100644 index 2a8cf06a..00000000 --- a/vendor/github.com/muesli/termenv/notification.go +++ /dev/null @@ -1,11 +0,0 @@ -package termenv - -// Notify triggers a notification using OSC777. -func Notify(title, body string) { - output.Notify(title, body) -} - -// Notify triggers a notification using OSC777. -func (o *Output) Notify(title, body string) { - _, _ = o.WriteString(OSC + "777;notify;" + title + ";" + body + ST) -} diff --git a/vendor/github.com/muesli/termenv/output.go b/vendor/github.com/muesli/termenv/output.go deleted file mode 100644 index e4434de0..00000000 --- a/vendor/github.com/muesli/termenv/output.go +++ /dev/null @@ -1,205 +0,0 @@ -package termenv - -import ( - "io" - "os" - "sync" -) - -// output is the default global output. -var output = NewOutput(os.Stdout) - -// File represents a file descriptor. -// -// Deprecated: Use *os.File instead. -type File interface { - io.ReadWriter - Fd() uintptr -} - -// OutputOption sets an option on Output. -type OutputOption = func(*Output) - -// Output is a terminal output. -type Output struct { - Profile - w io.Writer - environ Environ - - assumeTTY bool - unsafe bool - cache bool - fgSync *sync.Once - fgColor Color - bgSync *sync.Once - bgColor Color -} - -// Environ is an interface for getting environment variables. -type Environ interface { - Environ() []string - Getenv(string) string -} - -type osEnviron struct{} - -func (oe *osEnviron) Environ() []string { - return os.Environ() -} - -func (oe *osEnviron) Getenv(key string) string { - return os.Getenv(key) -} - -// DefaultOutput returns the default global output. -func DefaultOutput() *Output { - return output -} - -// SetDefaultOutput sets the default global output. -func SetDefaultOutput(o *Output) { - output = o -} - -// NewOutput returns a new Output for the given writer. -func NewOutput(w io.Writer, opts ...OutputOption) *Output { - o := &Output{ - w: w, - environ: &osEnviron{}, - Profile: -1, - fgSync: &sync.Once{}, - fgColor: NoColor{}, - bgSync: &sync.Once{}, - bgColor: NoColor{}, - } - - if o.w == nil { - o.w = os.Stdout - } - for _, opt := range opts { - opt(o) - } - if o.Profile < 0 { - o.Profile = o.EnvColorProfile() - } - - return o -} - -// WithEnvironment returns a new OutputOption for the given environment. -func WithEnvironment(environ Environ) OutputOption { - return func(o *Output) { - o.environ = environ - } -} - -// WithProfile returns a new OutputOption for the given profile. -func WithProfile(profile Profile) OutputOption { - return func(o *Output) { - o.Profile = profile - } -} - -// WithColorCache returns a new OutputOption with fore- and background color values -// pre-fetched and cached. -func WithColorCache(v bool) OutputOption { - return func(o *Output) { - o.cache = v - - // cache the values now - _ = o.ForegroundColor() - _ = o.BackgroundColor() - } -} - -// WithTTY returns a new OutputOption to assume whether or not the output is a TTY. -// This is useful when mocking console output. -func WithTTY(v bool) OutputOption { - return func(o *Output) { - o.assumeTTY = v - } -} - -// WithUnsafe returns a new OutputOption with unsafe mode enabled. Unsafe mode doesn't -// check whether or not the terminal is a TTY. -// -// This option supersedes WithTTY. -// -// This is useful when mocking console output and enforcing ANSI escape output -// e.g. on SSH sessions. -func WithUnsafe() OutputOption { - return func(o *Output) { - o.unsafe = true - } -} - -// ForegroundColor returns the terminal's default foreground color. -func (o *Output) ForegroundColor() Color { - f := func() { - if !o.isTTY() { - return - } - - o.fgColor = o.foregroundColor() - } - - if o.cache { - o.fgSync.Do(f) - } else { - f() - } - - return o.fgColor -} - -// BackgroundColor returns the terminal's default background color. -func (o *Output) BackgroundColor() Color { - f := func() { - if !o.isTTY() { - return - } - - o.bgColor = o.backgroundColor() - } - - if o.cache { - o.bgSync.Do(f) - } else { - f() - } - - return o.bgColor -} - -// HasDarkBackground returns whether terminal uses a dark-ish background. -func (o *Output) HasDarkBackground() bool { - c := ConvertToRGB(o.BackgroundColor()) - _, _, l := c.Hsl() - return l < 0.5 //nolint:mnd -} - -// TTY returns the terminal's file descriptor. This may be nil if the output is -// not a terminal. -// -// Deprecated: Use Writer() instead. -func (o Output) TTY() File { - if f, ok := o.w.(File); ok { - return f - } - return nil -} - -// Writer returns the underlying writer. This may be of type io.Writer, -// io.ReadWriter, or *os.File. -func (o Output) Writer() io.Writer { - return o.w -} - -func (o Output) Write(p []byte) (int, error) { - return o.w.Write(p) //nolint:wrapcheck -} - -// WriteString writes the given string to the output. -func (o Output) WriteString(s string) (int, error) { - return o.Write([]byte(s)) -} diff --git a/vendor/github.com/muesli/termenv/profile.go b/vendor/github.com/muesli/termenv/profile.go deleted file mode 100644 index 7d38f5fb..00000000 --- a/vendor/github.com/muesli/termenv/profile.go +++ /dev/null @@ -1,112 +0,0 @@ -package termenv - -import ( - "image/color" - "strconv" - "strings" - - "github.com/lucasb-eyer/go-colorful" -) - -// Profile is a color profile: Ascii, ANSI, ANSI256, or TrueColor. -type Profile int - -const ( - // TrueColor, 24-bit color profile. - TrueColor = Profile(iota) - // ANSI256, 8-bit color profile. - ANSI256 - // ANSI, 4-bit color profile. - ANSI - // Ascii, uncolored profile. - Ascii //nolint:revive -) - -// Name returns the profile name as a string. -func (p Profile) Name() string { - switch p { - case Ascii: - return "Ascii" - case ANSI: - return "ANSI" - case ANSI256: - return "ANSI256" - case TrueColor: - return "TrueColor" - } - return "Unknown" -} - -// String returns a new Style. -func (p Profile) String(s ...string) Style { - return Style{ - profile: p, - string: strings.Join(s, " "), - } -} - -// Convert transforms a given Color to a Color supported within the Profile. -func (p Profile) Convert(c Color) Color { - if p == Ascii { - return NoColor{} - } - - switch v := c.(type) { - case ANSIColor: - return v - - case ANSI256Color: - if p == ANSI { - return ansi256ToANSIColor(v) - } - return v - - case RGBColor: - h, err := colorful.Hex(string(v)) - if err != nil { - return nil - } - if p != TrueColor { - ac := hexToANSI256Color(h) - if p == ANSI { - return ansi256ToANSIColor(ac) - } - return ac - } - return v - } - - return c -} - -// Color creates a Color from a string. Valid inputs are hex colors, as well as -// ANSI color codes (0-15, 16-255). -func (p Profile) Color(s string) Color { - if len(s) == 0 { - return nil - } - - var c Color - if strings.HasPrefix(s, "#") { - c = RGBColor(s) - } else { - i, err := strconv.Atoi(s) - if err != nil { - return nil - } - - if i < 16 { //nolint:mnd - c = ANSIColor(i) - } else { - c = ANSI256Color(i) - } - } - - return p.Convert(c) -} - -// FromColor creates a Color from a color.Color. -func (p Profile) FromColor(c color.Color) Color { - col, _ := colorful.MakeColor(c) - return p.Color(col.Hex()) -} diff --git a/vendor/github.com/muesli/termenv/screen.go b/vendor/github.com/muesli/termenv/screen.go deleted file mode 100644 index 75c11d01..00000000 --- a/vendor/github.com/muesli/termenv/screen.go +++ /dev/null @@ -1,590 +0,0 @@ -package termenv - -import ( - "fmt" - "strings" -) - -// Sequence definitions. -const ( - // Cursor positioning. - CursorUpSeq = "%dA" - CursorDownSeq = "%dB" - CursorForwardSeq = "%dC" - CursorBackSeq = "%dD" - CursorNextLineSeq = "%dE" - CursorPreviousLineSeq = "%dF" - CursorHorizontalSeq = "%dG" - CursorPositionSeq = "%d;%dH" - EraseDisplaySeq = "%dJ" - EraseLineSeq = "%dK" - ScrollUpSeq = "%dS" - ScrollDownSeq = "%dT" - SaveCursorPositionSeq = "s" - RestoreCursorPositionSeq = "u" - ChangeScrollingRegionSeq = "%d;%dr" - InsertLineSeq = "%dL" - DeleteLineSeq = "%dM" - - // Explicit values for EraseLineSeq. - EraseLineRightSeq = "0K" - EraseLineLeftSeq = "1K" - EraseEntireLineSeq = "2K" - - // Mouse. - EnableMousePressSeq = "?9h" // press only (X10) - DisableMousePressSeq = "?9l" - EnableMouseSeq = "?1000h" // press, release, wheel - DisableMouseSeq = "?1000l" - EnableMouseHiliteSeq = "?1001h" // highlight - DisableMouseHiliteSeq = "?1001l" - EnableMouseCellMotionSeq = "?1002h" // press, release, move on pressed, wheel - DisableMouseCellMotionSeq = "?1002l" - EnableMouseAllMotionSeq = "?1003h" // press, release, move, wheel - DisableMouseAllMotionSeq = "?1003l" - EnableMouseExtendedModeSeq = "?1006h" // press, release, move, wheel, extended coordinates - DisableMouseExtendedModeSeq = "?1006l" - EnableMousePixelsModeSeq = "?1016h" // press, release, move, wheel, extended pixel coordinates - DisableMousePixelsModeSeq = "?1016l" - - // Screen. - RestoreScreenSeq = "?47l" - SaveScreenSeq = "?47h" - AltScreenSeq = "?1049h" - ExitAltScreenSeq = "?1049l" - - // Bracketed paste. - // https://en.wikipedia.org/wiki/Bracketed-paste - EnableBracketedPasteSeq = "?2004h" - DisableBracketedPasteSeq = "?2004l" - StartBracketedPasteSeq = "200~" - EndBracketedPasteSeq = "201~" - - // Session. - SetWindowTitleSeq = "2;%s" + string(BEL) - SetForegroundColorSeq = "10;%s" + string(BEL) - SetBackgroundColorSeq = "11;%s" + string(BEL) - SetCursorColorSeq = "12;%s" + string(BEL) - ShowCursorSeq = "?25h" - HideCursorSeq = "?25l" -) - -// Reset the terminal to its default style, removing any active styles. -func (o Output) Reset() { - fmt.Fprint(o.w, CSI+ResetSeq+"m") //nolint:errcheck -} - -// SetForegroundColor sets the default foreground color. -func (o Output) SetForegroundColor(color Color) { - fmt.Fprintf(o.w, OSC+SetForegroundColorSeq, color) //nolint:errcheck -} - -// SetBackgroundColor sets the default background color. -func (o Output) SetBackgroundColor(color Color) { - fmt.Fprintf(o.w, OSC+SetBackgroundColorSeq, color) //nolint:errcheck -} - -// SetCursorColor sets the cursor color. -func (o Output) SetCursorColor(color Color) { - fmt.Fprintf(o.w, OSC+SetCursorColorSeq, color) //nolint:errcheck -} - -// RestoreScreen restores a previously saved screen state. -func (o Output) RestoreScreen() { - fmt.Fprint(o.w, CSI+RestoreScreenSeq) //nolint:errcheck -} - -// SaveScreen saves the screen state. -func (o Output) SaveScreen() { - fmt.Fprint(o.w, CSI+SaveScreenSeq) //nolint:errcheck -} - -// AltScreen switches to the alternate screen buffer. The former view can be -// restored with ExitAltScreen(). -func (o Output) AltScreen() { - fmt.Fprint(o.w, CSI+AltScreenSeq) //nolint:errcheck -} - -// ExitAltScreen exits the alternate screen buffer and returns to the former -// terminal view. -func (o Output) ExitAltScreen() { - fmt.Fprint(o.w, CSI+ExitAltScreenSeq) //nolint:errcheck -} - -// ClearScreen clears the visible portion of the terminal. -func (o Output) ClearScreen() { - fmt.Fprintf(o.w, CSI+EraseDisplaySeq, 2) //nolint:errcheck,mnd - o.MoveCursor(1, 1) -} - -// MoveCursor moves the cursor to a given position. -func (o Output) MoveCursor(row int, column int) { - fmt.Fprintf(o.w, CSI+CursorPositionSeq, row, column) //nolint:errcheck -} - -// HideCursor hides the cursor. -func (o Output) HideCursor() { - fmt.Fprint(o.w, CSI+HideCursorSeq) //nolint:errcheck -} - -// ShowCursor shows the cursor. -func (o Output) ShowCursor() { - fmt.Fprint(o.w, CSI+ShowCursorSeq) //nolint:errcheck -} - -// SaveCursorPosition saves the cursor position. -func (o Output) SaveCursorPosition() { - fmt.Fprint(o.w, CSI+SaveCursorPositionSeq) //nolint:errcheck -} - -// RestoreCursorPosition restores a saved cursor position. -func (o Output) RestoreCursorPosition() { - fmt.Fprint(o.w, CSI+RestoreCursorPositionSeq) //nolint:errcheck -} - -// CursorUp moves the cursor up a given number of lines. -func (o Output) CursorUp(n int) { - fmt.Fprintf(o.w, CSI+CursorUpSeq, n) //nolint:errcheck -} - -// CursorDown moves the cursor down a given number of lines. -func (o Output) CursorDown(n int) { - fmt.Fprintf(o.w, CSI+CursorDownSeq, n) //nolint:errcheck -} - -// CursorForward moves the cursor up a given number of lines. -func (o Output) CursorForward(n int) { - fmt.Fprintf(o.w, CSI+CursorForwardSeq, n) //nolint:errcheck -} - -// CursorBack moves the cursor backwards a given number of cells. -func (o Output) CursorBack(n int) { - fmt.Fprintf(o.w, CSI+CursorBackSeq, n) //nolint:errcheck -} - -// CursorNextLine moves the cursor down a given number of lines and places it at -// the beginning of the line. -func (o Output) CursorNextLine(n int) { - fmt.Fprintf(o.w, CSI+CursorNextLineSeq, n) //nolint:errcheck -} - -// CursorPrevLine moves the cursor up a given number of lines and places it at -// the beginning of the line. -func (o Output) CursorPrevLine(n int) { - fmt.Fprintf(o.w, CSI+CursorPreviousLineSeq, n) //nolint:errcheck -} - -// ClearLine clears the current line. -func (o Output) ClearLine() { - fmt.Fprint(o.w, CSI+EraseEntireLineSeq) //nolint:errcheck -} - -// ClearLineLeft clears the line to the left of the cursor. -func (o Output) ClearLineLeft() { - fmt.Fprint(o.w, CSI+EraseLineLeftSeq) //nolint:errcheck -} - -// ClearLineRight clears the line to the right of the cursor. -func (o Output) ClearLineRight() { - fmt.Fprint(o.w, CSI+EraseLineRightSeq) //nolint:errcheck -} - -// ClearLines clears a given number of lines. -func (o Output) ClearLines(n int) { - clearLine := fmt.Sprintf(CSI+EraseLineSeq, 2) //nolint:mnd - cursorUp := fmt.Sprintf(CSI+CursorUpSeq, 1) - fmt.Fprint(o.w, clearLine+strings.Repeat(cursorUp+clearLine, n)) //nolint:errcheck -} - -// ChangeScrollingRegion sets the scrolling region of the terminal. -func (o Output) ChangeScrollingRegion(top, bottom int) { - fmt.Fprintf(o.w, CSI+ChangeScrollingRegionSeq, top, bottom) //nolint:errcheck -} - -// InsertLines inserts the given number of lines at the top of the scrollable -// region, pushing lines below down. -func (o Output) InsertLines(n int) { - fmt.Fprintf(o.w, CSI+InsertLineSeq, n) //nolint:errcheck -} - -// DeleteLines deletes the given number of lines, pulling any lines in -// the scrollable region below up. -func (o Output) DeleteLines(n int) { - fmt.Fprintf(o.w, CSI+DeleteLineSeq, n) //nolint:errcheck -} - -// EnableMousePress enables X10 mouse mode. Button press events are sent only. -func (o Output) EnableMousePress() { - fmt.Fprint(o.w, CSI+EnableMousePressSeq) //nolint:errcheck -} - -// DisableMousePress disables X10 mouse mode. -func (o Output) DisableMousePress() { - fmt.Fprint(o.w, CSI+DisableMousePressSeq) //nolint:errcheck -} - -// EnableMouse enables Mouse Tracking mode. -func (o Output) EnableMouse() { - fmt.Fprint(o.w, CSI+EnableMouseSeq) //nolint:errcheck -} - -// DisableMouse disables Mouse Tracking mode. -func (o Output) DisableMouse() { - fmt.Fprint(o.w, CSI+DisableMouseSeq) //nolint:errcheck -} - -// EnableMouseHilite enables Hilite Mouse Tracking mode. -func (o Output) EnableMouseHilite() { - fmt.Fprint(o.w, CSI+EnableMouseHiliteSeq) //nolint:errcheck -} - -// DisableMouseHilite disables Hilite Mouse Tracking mode. -func (o Output) DisableMouseHilite() { - fmt.Fprint(o.w, CSI+DisableMouseHiliteSeq) //nolint:errcheck -} - -// EnableMouseCellMotion enables Cell Motion Mouse Tracking mode. -func (o Output) EnableMouseCellMotion() { - fmt.Fprint(o.w, CSI+EnableMouseCellMotionSeq) //nolint:errcheck -} - -// DisableMouseCellMotion disables Cell Motion Mouse Tracking mode. -func (o Output) DisableMouseCellMotion() { - fmt.Fprint(o.w, CSI+DisableMouseCellMotionSeq) //nolint:errcheck -} - -// EnableMouseAllMotion enables All Motion Mouse mode. -func (o Output) EnableMouseAllMotion() { - fmt.Fprint(o.w, CSI+EnableMouseAllMotionSeq) //nolint:errcheck -} - -// DisableMouseAllMotion disables All Motion Mouse mode. -func (o Output) DisableMouseAllMotion() { - fmt.Fprint(o.w, CSI+DisableMouseAllMotionSeq) //nolint:errcheck -} - -// EnableMouseExtendedMotion enables Extended Mouse mode (SGR). This should be -// enabled in conjunction with EnableMouseCellMotion, and EnableMouseAllMotion. -func (o Output) EnableMouseExtendedMode() { - fmt.Fprint(o.w, CSI+EnableMouseExtendedModeSeq) //nolint:errcheck -} - -// DisableMouseExtendedMotion disables Extended Mouse mode (SGR). -func (o Output) DisableMouseExtendedMode() { - fmt.Fprint(o.w, CSI+DisableMouseExtendedModeSeq) //nolint:errcheck -} - -// EnableMousePixelsMotion enables Pixel Motion Mouse mode (SGR-Pixels). This -// should be enabled in conjunction with EnableMouseCellMotion, and -// EnableMouseAllMotion. -func (o Output) EnableMousePixelsMode() { - fmt.Fprint(o.w, CSI+EnableMousePixelsModeSeq) //nolint:errcheck -} - -// DisableMousePixelsMotion disables Pixel Motion Mouse mode (SGR-Pixels). -func (o Output) DisableMousePixelsMode() { - fmt.Fprint(o.w, CSI+DisableMousePixelsModeSeq) //nolint:errcheck -} - -// SetWindowTitle sets the terminal window title. -func (o Output) SetWindowTitle(title string) { - fmt.Fprintf(o.w, OSC+SetWindowTitleSeq, title) //nolint:errcheck -} - -// EnableBracketedPaste enables bracketed paste. -func (o Output) EnableBracketedPaste() { - fmt.Fprintf(o.w, CSI+EnableBracketedPasteSeq) //nolint:errcheck -} - -// DisableBracketedPaste disables bracketed paste. -func (o Output) DisableBracketedPaste() { - fmt.Fprintf(o.w, CSI+DisableBracketedPasteSeq) //nolint:errcheck -} - -// Legacy functions. - -// Reset the terminal to its default style, removing any active styles. -// -// Deprecated: please use termenv.Output instead. -func Reset() { - output.Reset() -} - -// SetForegroundColor sets the default foreground color. -// -// Deprecated: please use termenv.Output instead. -func SetForegroundColor(color Color) { - output.SetForegroundColor(color) -} - -// SetBackgroundColor sets the default background color. -// -// Deprecated: please use termenv.Output instead. -func SetBackgroundColor(color Color) { - output.SetBackgroundColor(color) -} - -// SetCursorColor sets the cursor color. -// -// Deprecated: please use termenv.Output instead. -func SetCursorColor(color Color) { - output.SetCursorColor(color) -} - -// RestoreScreen restores a previously saved screen state. -// -// Deprecated: please use termenv.Output instead. -func RestoreScreen() { - output.RestoreScreen() -} - -// SaveScreen saves the screen state. -// -// Deprecated: please use termenv.Output instead. -func SaveScreen() { - output.SaveScreen() -} - -// AltScreen switches to the alternate screen buffer. The former view can be -// restored with ExitAltScreen(). -// -// Deprecated: please use termenv.Output instead. -func AltScreen() { - output.AltScreen() -} - -// ExitAltScreen exits the alternate screen buffer and returns to the former -// terminal view. -// -// Deprecated: please use termenv.Output instead. -func ExitAltScreen() { - output.ExitAltScreen() -} - -// ClearScreen clears the visible portion of the terminal. -// -// Deprecated: please use termenv.Output instead. -func ClearScreen() { - output.ClearScreen() -} - -// MoveCursor moves the cursor to a given position. -// -// Deprecated: please use termenv.Output instead. -func MoveCursor(row int, column int) { - output.MoveCursor(row, column) -} - -// HideCursor hides the cursor. -// -// Deprecated: please use termenv.Output instead. -func HideCursor() { - output.HideCursor() -} - -// ShowCursor shows the cursor. -// -// Deprecated: please use termenv.Output instead. -func ShowCursor() { - output.ShowCursor() -} - -// SaveCursorPosition saves the cursor position. -// -// Deprecated: please use termenv.Output instead. -func SaveCursorPosition() { - output.SaveCursorPosition() -} - -// RestoreCursorPosition restores a saved cursor position. -// -// Deprecated: please use termenv.Output instead. -func RestoreCursorPosition() { - output.RestoreCursorPosition() -} - -// CursorUp moves the cursor up a given number of lines. -// -// Deprecated: please use termenv.Output instead. -func CursorUp(n int) { - output.CursorUp(n) -} - -// CursorDown moves the cursor down a given number of lines. -// -// Deprecated: please use termenv.Output instead. -func CursorDown(n int) { - output.CursorDown(n) -} - -// CursorForward moves the cursor up a given number of lines. -// -// Deprecated: please use termenv.Output instead. -func CursorForward(n int) { - output.CursorForward(n) -} - -// CursorBack moves the cursor backwards a given number of cells. -// -// Deprecated: please use termenv.Output instead. -func CursorBack(n int) { - output.CursorBack(n) -} - -// CursorNextLine moves the cursor down a given number of lines and places it at -// the beginning of the line. -// -// Deprecated: please use termenv.Output instead. -func CursorNextLine(n int) { - output.CursorNextLine(n) -} - -// CursorPrevLine moves the cursor up a given number of lines and places it at -// the beginning of the line. -// -// Deprecated: please use termenv.Output instead. -func CursorPrevLine(n int) { - output.CursorPrevLine(n) -} - -// ClearLine clears the current line. -// -// Deprecated: please use termenv.Output instead. -func ClearLine() { - output.ClearLine() -} - -// ClearLineLeft clears the line to the left of the cursor. -// -// Deprecated: please use termenv.Output instead. -func ClearLineLeft() { - output.ClearLineLeft() -} - -// ClearLineRight clears the line to the right of the cursor. -// -// Deprecated: please use termenv.Output instead. -func ClearLineRight() { - output.ClearLineRight() -} - -// ClearLines clears a given number of lines. -// -// Deprecated: please use termenv.Output instead. -func ClearLines(n int) { - output.ClearLines(n) -} - -// ChangeScrollingRegion sets the scrolling region of the terminal. -// -// Deprecated: please use termenv.Output instead. -func ChangeScrollingRegion(top, bottom int) { - output.ChangeScrollingRegion(top, bottom) -} - -// InsertLines inserts the given number of lines at the top of the scrollable -// region, pushing lines below down. -// -// Deprecated: please use termenv.Output instead. -func InsertLines(n int) { - output.InsertLines(n) -} - -// DeleteLines deletes the given number of lines, pulling any lines in -// the scrollable region below up. -// -// Deprecated: please use termenv.Output instead. -func DeleteLines(n int) { - output.DeleteLines(n) -} - -// EnableMousePress enables X10 mouse mode. Button press events are sent only. -// -// Deprecated: please use termenv.Output instead. -func EnableMousePress() { - output.EnableMousePress() -} - -// DisableMousePress disables X10 mouse mode. -// -// Deprecated: please use termenv.Output instead. -func DisableMousePress() { - output.DisableMousePress() -} - -// EnableMouse enables Mouse Tracking mode. -// -// Deprecated: please use termenv.Output instead. -func EnableMouse() { - output.EnableMouse() -} - -// DisableMouse disables Mouse Tracking mode. -// -// Deprecated: please use termenv.Output instead. -func DisableMouse() { - output.DisableMouse() -} - -// EnableMouseHilite enables Hilite Mouse Tracking mode. -// -// Deprecated: please use termenv.Output instead. -func EnableMouseHilite() { - output.EnableMouseHilite() -} - -// DisableMouseHilite disables Hilite Mouse Tracking mode. -// -// Deprecated: please use termenv.Output instead. -func DisableMouseHilite() { - output.DisableMouseHilite() -} - -// EnableMouseCellMotion enables Cell Motion Mouse Tracking mode. -// -// Deprecated: please use termenv.Output instead. -func EnableMouseCellMotion() { - output.EnableMouseCellMotion() -} - -// DisableMouseCellMotion disables Cell Motion Mouse Tracking mode. -// -// Deprecated: please use termenv.Output instead. -func DisableMouseCellMotion() { - output.DisableMouseCellMotion() -} - -// EnableMouseAllMotion enables All Motion Mouse mode. -// -// Deprecated: please use termenv.Output instead. -func EnableMouseAllMotion() { - output.EnableMouseAllMotion() -} - -// DisableMouseAllMotion disables All Motion Mouse mode. -// -// Deprecated: please use termenv.Output instead. -func DisableMouseAllMotion() { - output.DisableMouseAllMotion() -} - -// SetWindowTitle sets the terminal window title. -// -// Deprecated: please use termenv.Output instead. -func SetWindowTitle(title string) { - output.SetWindowTitle(title) -} - -// EnableBracketedPaste enables bracketed paste. -// -// Deprecated: please use termenv.Output instead. -func EnableBracketedPaste() { - output.EnableBracketedPaste() -} - -// DisableBracketedPaste disables bracketed paste. -// -// Deprecated: please use termenv.Output instead. -func DisableBracketedPaste() { - output.DisableBracketedPaste() -} diff --git a/vendor/github.com/muesli/termenv/style.go b/vendor/github.com/muesli/termenv/style.go deleted file mode 100644 index dedc1f9f..00000000 --- a/vendor/github.com/muesli/termenv/style.go +++ /dev/null @@ -1,126 +0,0 @@ -package termenv - -import ( - "fmt" - "strings" - - "github.com/rivo/uniseg" -) - -// Sequence definitions. -const ( - ResetSeq = "0" - BoldSeq = "1" - FaintSeq = "2" - ItalicSeq = "3" - UnderlineSeq = "4" - BlinkSeq = "5" - ReverseSeq = "7" - CrossOutSeq = "9" - OverlineSeq = "53" -) - -// Style is a string that various rendering styles can be applied to. -type Style struct { - profile Profile - string - styles []string -} - -// String returns a new Style. -func String(s ...string) Style { - return Style{ - profile: ANSI, - string: strings.Join(s, " "), - } -} - -func (t Style) String() string { - return t.Styled(t.string) -} - -// Styled renders s with all applied styles. -func (t Style) Styled(s string) string { - if t.profile == Ascii { - return s - } - if len(t.styles) == 0 { - return s - } - - seq := strings.Join(t.styles, ";") - if seq == "" { - return s - } - - return fmt.Sprintf("%s%sm%s%sm", CSI, seq, s, CSI+ResetSeq) -} - -// Foreground sets a foreground color. -func (t Style) Foreground(c Color) Style { - if c != nil { - t.styles = append(t.styles, c.Sequence(false)) - } - return t -} - -// Background sets a background color. -func (t Style) Background(c Color) Style { - if c != nil { - t.styles = append(t.styles, c.Sequence(true)) - } - return t -} - -// Bold enables bold rendering. -func (t Style) Bold() Style { - t.styles = append(t.styles, BoldSeq) - return t -} - -// Faint enables faint rendering. -func (t Style) Faint() Style { - t.styles = append(t.styles, FaintSeq) - return t -} - -// Italic enables italic rendering. -func (t Style) Italic() Style { - t.styles = append(t.styles, ItalicSeq) - return t -} - -// Underline enables underline rendering. -func (t Style) Underline() Style { - t.styles = append(t.styles, UnderlineSeq) - return t -} - -// Overline enables overline rendering. -func (t Style) Overline() Style { - t.styles = append(t.styles, OverlineSeq) - return t -} - -// Blink enables blink mode. -func (t Style) Blink() Style { - t.styles = append(t.styles, BlinkSeq) - return t -} - -// Reverse enables reverse color mode. -func (t Style) Reverse() Style { - t.styles = append(t.styles, ReverseSeq) - return t -} - -// CrossOut enables crossed-out rendering. -func (t Style) CrossOut() Style { - t.styles = append(t.styles, CrossOutSeq) - return t -} - -// Width returns the width required to print all runes in Style. -func (t Style) Width() int { - return uniseg.StringWidth(t.string) -} diff --git a/vendor/github.com/muesli/termenv/templatehelper.go b/vendor/github.com/muesli/termenv/templatehelper.go deleted file mode 100644 index 4c7c80f5..00000000 --- a/vendor/github.com/muesli/termenv/templatehelper.go +++ /dev/null @@ -1,88 +0,0 @@ -package termenv - -import ( - "text/template" -) - -// TemplateFuncs returns template helpers for the given output. -func (o Output) TemplateFuncs() template.FuncMap { - return TemplateFuncs(o.Profile) -} - -// TemplateFuncs contains a few useful template helpers. -// -//nolint:mnd -func TemplateFuncs(p Profile) template.FuncMap { - if p == Ascii { - return noopTemplateFuncs - } - - return template.FuncMap{ - "Color": func(values ...interface{}) string { - s := p.String(values[len(values)-1].(string)) - switch len(values) { - case 2: - s = s.Foreground(p.Color(values[0].(string))) - case 3: - s = s. - Foreground(p.Color(values[0].(string))). - Background(p.Color(values[1].(string))) - } - - return s.String() - }, - "Foreground": func(values ...interface{}) string { - s := p.String(values[len(values)-1].(string)) - if len(values) == 2 { - s = s.Foreground(p.Color(values[0].(string))) - } - - return s.String() - }, - "Background": func(values ...interface{}) string { - s := p.String(values[len(values)-1].(string)) - if len(values) == 2 { - s = s.Background(p.Color(values[0].(string))) - } - - return s.String() - }, - "Bold": styleFunc(p, Style.Bold), - "Faint": styleFunc(p, Style.Faint), - "Italic": styleFunc(p, Style.Italic), - "Underline": styleFunc(p, Style.Underline), - "Overline": styleFunc(p, Style.Overline), - "Blink": styleFunc(p, Style.Blink), - "Reverse": styleFunc(p, Style.Reverse), - "CrossOut": styleFunc(p, Style.CrossOut), - } -} - -func styleFunc(p Profile, f func(Style) Style) func(...interface{}) string { - return func(values ...interface{}) string { - s := p.String(values[0].(string)) - return f(s).String() - } -} - -var noopTemplateFuncs = template.FuncMap{ - "Color": noColorFunc, - "Foreground": noColorFunc, - "Background": noColorFunc, - "Bold": noStyleFunc, - "Faint": noStyleFunc, - "Italic": noStyleFunc, - "Underline": noStyleFunc, - "Overline": noStyleFunc, - "Blink": noStyleFunc, - "Reverse": noStyleFunc, - "CrossOut": noStyleFunc, -} - -func noColorFunc(values ...interface{}) string { - return values[len(values)-1].(string) -} - -func noStyleFunc(values ...interface{}) string { - return values[0].(string) -} diff --git a/vendor/github.com/muesli/termenv/termenv.go b/vendor/github.com/muesli/termenv/termenv.go deleted file mode 100644 index d702cd55..00000000 --- a/vendor/github.com/muesli/termenv/termenv.go +++ /dev/null @@ -1,115 +0,0 @@ -package termenv - -import ( - "errors" - "os" - - "github.com/mattn/go-isatty" -) - -var ( - // ErrStatusReport gets returned when the terminal can't be queried. - ErrStatusReport = errors.New("unable to retrieve status report") -) - -const ( - // Escape character. - ESC = '\x1b' - // Bell. - BEL = '\a' - // Control Sequence Introducer. - CSI = string(ESC) + "[" - // Operating System Command. - OSC = string(ESC) + "]" - // String Terminator. - ST = string(ESC) + `\` -) - -func (o *Output) isTTY() bool { - if o.assumeTTY || o.unsafe { - return true - } - if len(o.environ.Getenv("CI")) > 0 { - return false - } - if f, ok := o.Writer().(*os.File); ok { - return isatty.IsTerminal(f.Fd()) - } - - return false -} - -// ColorProfile returns the supported color profile: -// Ascii, ANSI, ANSI256, or TrueColor. -func ColorProfile() Profile { - return output.ColorProfile() -} - -// ForegroundColor returns the terminal's default foreground color. -func ForegroundColor() Color { - return output.ForegroundColor() -} - -// BackgroundColor returns the terminal's default background color. -func BackgroundColor() Color { - return output.BackgroundColor() -} - -// HasDarkBackground returns whether terminal uses a dark-ish background. -func HasDarkBackground() bool { - return output.HasDarkBackground() -} - -// EnvNoColor returns true if the environment variables explicitly disable color output -// by setting NO_COLOR (https://no-color.org/) -// or CLICOLOR/CLICOLOR_FORCE (https://bixense.com/clicolors/) -// If NO_COLOR is set, this will return true, ignoring CLICOLOR/CLICOLOR_FORCE -// If CLICOLOR=="0", it will be true only if CLICOLOR_FORCE is also "0" or is unset. -func (o *Output) EnvNoColor() bool { - return o.environ.Getenv("NO_COLOR") != "" || (o.environ.Getenv("CLICOLOR") == "0" && !o.cliColorForced()) -} - -// EnvNoColor returns true if the environment variables explicitly disable color output -// by setting NO_COLOR (https://no-color.org/) -// or CLICOLOR/CLICOLOR_FORCE (https://bixense.com/clicolors/) -// If NO_COLOR is set, this will return true, ignoring CLICOLOR/CLICOLOR_FORCE -// If CLICOLOR=="0", it will be true only if CLICOLOR_FORCE is also "0" or is unset. -func EnvNoColor() bool { - return output.EnvNoColor() -} - -// EnvColorProfile returns the color profile based on environment variables set -// Supports NO_COLOR (https://no-color.org/) -// and CLICOLOR/CLICOLOR_FORCE (https://bixense.com/clicolors/) -// If none of these environment variables are set, this behaves the same as ColorProfile() -// It will return the Ascii color profile if EnvNoColor() returns true -// If the terminal does not support any colors, but CLICOLOR_FORCE is set and not "0" -// then the ANSI color profile will be returned. -func EnvColorProfile() Profile { - return output.EnvColorProfile() -} - -// EnvColorProfile returns the color profile based on environment variables set -// Supports NO_COLOR (https://no-color.org/) -// and CLICOLOR/CLICOLOR_FORCE (https://bixense.com/clicolors/) -// If none of these environment variables are set, this behaves the same as ColorProfile() -// It will return the Ascii color profile if EnvNoColor() returns true -// If the terminal does not support any colors, but CLICOLOR_FORCE is set and not "0" -// then the ANSI color profile will be returned. -func (o *Output) EnvColorProfile() Profile { - if o.EnvNoColor() { - return Ascii - } - p := o.ColorProfile() - if o.cliColorForced() && p == Ascii { - return ANSI - } - return p -} - -func (o *Output) cliColorForced() bool { - if forced := o.environ.Getenv("CLICOLOR_FORCE"); forced != "" { - return forced != "0" - } - return false -} diff --git a/vendor/github.com/muesli/termenv/termenv_other.go b/vendor/github.com/muesli/termenv/termenv_other.go deleted file mode 100644 index 93a43b6a..00000000 --- a/vendor/github.com/muesli/termenv/termenv_other.go +++ /dev/null @@ -1,30 +0,0 @@ -//go:build js || plan9 || aix -// +build js plan9 aix - -package termenv - -import "io" - -// ColorProfile returns the supported color profile: -// ANSI256 -func (o Output) ColorProfile() Profile { - return ANSI256 -} - -func (o Output) foregroundColor() Color { - // default gray - return ANSIColor(7) -} - -func (o Output) backgroundColor() Color { - // default black - return ANSIColor(0) -} - -// EnableVirtualTerminalProcessing enables virtual terminal processing on -// Windows for w and returns a function that restores w to its previous state. -// On non-Windows platforms, or if w does not refer to a terminal, then it -// returns a non-nil no-op function and no error. -func EnableVirtualTerminalProcessing(w io.Writer) (func() error, error) { - return func() error { return nil }, nil -} diff --git a/vendor/github.com/muesli/termenv/termenv_posix.go b/vendor/github.com/muesli/termenv/termenv_posix.go deleted file mode 100644 index c971dd99..00000000 --- a/vendor/github.com/muesli/termenv/termenv_posix.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || zos -// +build darwin dragonfly freebsd linux netbsd openbsd zos - -package termenv - -import ( - "golang.org/x/sys/unix" -) - -func isForeground(fd int) bool { - pgrp, err := unix.IoctlGetInt(fd, unix.TIOCGPGRP) - if err != nil { - return false - } - - return pgrp == unix.Getpgrp() -} diff --git a/vendor/github.com/muesli/termenv/termenv_solaris.go b/vendor/github.com/muesli/termenv/termenv_solaris.go deleted file mode 100644 index 27a95a93..00000000 --- a/vendor/github.com/muesli/termenv/termenv_solaris.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build solaris || illumos -// +build solaris illumos - -package termenv - -import ( - "golang.org/x/sys/unix" -) - -func isForeground(fd int) bool { - pgrp, err := unix.IoctlGetInt(fd, unix.TIOCGPGRP) - if err != nil { - return false - } - - g, err := unix.Getpgrp() - if err != nil { - return false - } - - return pgrp == g -} diff --git a/vendor/github.com/muesli/termenv/termenv_unix.go b/vendor/github.com/muesli/termenv/termenv_unix.go deleted file mode 100644 index bef49ca3..00000000 --- a/vendor/github.com/muesli/termenv/termenv_unix.go +++ /dev/null @@ -1,301 +0,0 @@ -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build darwin dragonfly freebsd linux netbsd openbsd solaris zos - -package termenv - -import ( - "fmt" - "io" - "strconv" - "strings" - "time" - - "golang.org/x/sys/unix" -) - -const ( - // timeout for OSC queries. - OSCTimeout = 5 * time.Second -) - -// ColorProfile returns the supported color profile: -// Ascii, ANSI, ANSI256, or TrueColor. -func (o *Output) ColorProfile() Profile { - if !o.isTTY() { - return Ascii - } - - if o.environ.Getenv("GOOGLE_CLOUD_SHELL") == "true" { - return TrueColor - } - - term := o.environ.Getenv("TERM") - colorTerm := o.environ.Getenv("COLORTERM") - - switch strings.ToLower(colorTerm) { - case "24bit": - fallthrough - case "truecolor": - if strings.HasPrefix(term, "screen") { - // tmux supports TrueColor, screen only ANSI256 - if o.environ.Getenv("TERM_PROGRAM") != "tmux" { - return ANSI256 - } - } - return TrueColor - case "yes": - fallthrough - case "true": - return ANSI256 - } - - switch term { - case - "alacritty", - "contour", - "rio", - "wezterm", - "xterm-ghostty", - "xterm-kitty": - return TrueColor - case "linux", "xterm": - return ANSI - } - - if strings.Contains(term, "256color") { - return ANSI256 - } - if strings.Contains(term, "color") { - return ANSI - } - if strings.Contains(term, "ansi") { - return ANSI - } - - return Ascii -} - -//nolint:mnd -func (o Output) foregroundColor() Color { - s, err := o.termStatusReport(10) - if err == nil { - c, err := xTermColor(s) - if err == nil { - return c - } - } - - colorFGBG := o.environ.Getenv("COLORFGBG") - if strings.Contains(colorFGBG, ";") { - c := strings.Split(colorFGBG, ";") - i, err := strconv.Atoi(c[0]) - if err == nil { - return ANSIColor(i) - } - } - - // default gray - return ANSIColor(7) -} - -//nolint:mnd -func (o Output) backgroundColor() Color { - s, err := o.termStatusReport(11) - if err == nil { - c, err := xTermColor(s) - if err == nil { - return c - } - } - - colorFGBG := o.environ.Getenv("COLORFGBG") - if strings.Contains(colorFGBG, ";") { - c := strings.Split(colorFGBG, ";") - i, err := strconv.Atoi(c[len(c)-1]) - if err == nil { - return ANSIColor(i) - } - } - - // default black - return ANSIColor(0) -} - -func (o *Output) waitForData(timeout time.Duration) error { - fd := o.TTY().Fd() - tv := unix.NsecToTimeval(int64(timeout)) - var readfds unix.FdSet - readfds.Set(int(fd)) //nolint:gosec - - for { - n, err := unix.Select(int(fd)+1, &readfds, nil, nil, &tv) //nolint:gosec - if err == unix.EINTR { - continue - } - if err != nil { - return err //nolint:wrapcheck - } - if n == 0 { - return fmt.Errorf("timeout") - } - - break - } - - return nil -} - -func (o *Output) readNextByte() (byte, error) { - if !o.unsafe { - if err := o.waitForData(OSCTimeout); err != nil { - return 0, err - } - } - - var b [1]byte - n, err := o.TTY().Read(b[:]) - if err != nil { - return 0, err //nolint:wrapcheck - } - - if n == 0 { - panic("read returned no data") - } - - return b[0], nil -} - -// readNextResponse reads either an OSC response or a cursor position response: -// - OSC response: "\x1b]11;rgb:1111/1111/1111\x1b\\" -// - cursor position response: "\x1b[42;1R" -func (o *Output) readNextResponse() (response string, isOSC bool, err error) { - start, err := o.readNextByte() - if err != nil { - return "", false, err - } - - // first byte must be ESC - for start != ESC { - start, err = o.readNextByte() - if err != nil { - return "", false, err - } - } - - response += string(start) - - // next byte is either '[' (cursor position response) or ']' (OSC response) - tpe, err := o.readNextByte() - if err != nil { - return "", false, err - } - - response += string(tpe) - - var oscResponse bool - switch tpe { - case '[': - oscResponse = false - case ']': - oscResponse = true - default: - return "", false, ErrStatusReport - } - - for { - b, err := o.readNextByte() - if err != nil { - return "", false, err - } - - response += string(b) - - if oscResponse { - // OSC can be terminated by BEL (\a) or ST (ESC) - if b == BEL || strings.HasSuffix(response, string(ESC)) { - return response, true, nil - } - } else { - // cursor position response is terminated by 'R' - if b == 'R' { - return response, false, nil - } - } - - // both responses have less than 25 bytes, so if we read more, that's an error - if len(response) > 25 { //nolint:mnd - break - } - } - - return "", false, ErrStatusReport -} - -func (o Output) termStatusReport(sequence int) (string, error) { - // screen/tmux can't support OSC, because they can be connected to multiple - // terminals concurrently. - term := o.environ.Getenv("TERM") - if strings.HasPrefix(term, "screen") || strings.HasPrefix(term, "tmux") || strings.HasPrefix(term, "dumb") { - return "", ErrStatusReport - } - - tty := o.TTY() - if tty == nil { - return "", ErrStatusReport - } - - if !o.unsafe { - fd := int(tty.Fd()) //nolint:gosec - // if in background, we can't control the terminal - if !isForeground(fd) { - return "", ErrStatusReport - } - - t, err := unix.IoctlGetTermios(fd, tcgetattr) - if err != nil { - return "", fmt.Errorf("%s: %s", ErrStatusReport, err) - } - defer unix.IoctlSetTermios(fd, tcsetattr, t) //nolint:errcheck - - noecho := *t - noecho.Lflag = noecho.Lflag &^ unix.ECHO - noecho.Lflag = noecho.Lflag &^ unix.ICANON - if err := unix.IoctlSetTermios(fd, tcsetattr, &noecho); err != nil { - return "", fmt.Errorf("%s: %s", ErrStatusReport, err) - } - } - - // first, send OSC query, which is ignored by terminal which do not support it - fmt.Fprintf(tty, OSC+"%d;?"+ST, sequence) //nolint:errcheck - - // then, query cursor position, should be supported by all terminals - fmt.Fprintf(tty, CSI+"6n") //nolint:errcheck - - // read the next response - res, isOSC, err := o.readNextResponse() - if err != nil { - return "", fmt.Errorf("%s: %s", ErrStatusReport, err) - } - - // if this is not OSC response, then the terminal does not support it - if !isOSC { - return "", ErrStatusReport - } - - // read the cursor query response next and discard the result - _, _, err = o.readNextResponse() - if err != nil { - return "", err - } - - // fmt.Println("Rcvd", res[1:]) - return res, nil -} - -// EnableVirtualTerminalProcessing enables virtual terminal processing on -// Windows for w and returns a function that restores w to its previous state. -// On non-Windows platforms, or if w does not refer to a terminal, then it -// returns a non-nil no-op function and no error. -func EnableVirtualTerminalProcessing(_ io.Writer) (func() error, error) { - return func() error { return nil }, nil -} diff --git a/vendor/github.com/muesli/termenv/termenv_windows.go b/vendor/github.com/muesli/termenv/termenv_windows.go deleted file mode 100644 index f9b1def0..00000000 --- a/vendor/github.com/muesli/termenv/termenv_windows.go +++ /dev/null @@ -1,140 +0,0 @@ -//go:build windows -// +build windows - -package termenv - -import ( - "fmt" - "os" - "strconv" - - "golang.org/x/sys/windows" -) - -func (o *Output) ColorProfile() Profile { - if !o.isTTY() { - return Ascii - } - - if o.environ.Getenv("ConEmuANSI") == "ON" { - return TrueColor - } - - winVersion, _, buildNumber := windows.RtlGetNtVersionNumbers() - if buildNumber < 10586 || winVersion < 10 { - // No ANSI support before Windows 10 build 10586. - if o.environ.Getenv("ANSICON") != "" { - conVersion := o.environ.Getenv("ANSICON_VER") - cv, err := strconv.ParseInt(conVersion, 10, 64) - if err != nil || cv < 181 { - // No 8 bit color support before v1.81 release. - return ANSI - } - - return ANSI256 - } - - return Ascii - } - if buildNumber < 14931 { - // No true color support before build 14931. - return ANSI256 - } - - return TrueColor -} - -func (o Output) foregroundColor() Color { - // default gray - return ANSIColor(7) -} - -func (o Output) backgroundColor() Color { - // default black - return ANSIColor(0) -} - -// EnableWindowsANSIConsole enables virtual terminal processing on Windows -// platforms. This allows the use of ANSI escape sequences in Windows console -// applications. Ensure this gets called before anything gets rendered with -// termenv. -// -// Returns the original console mode and an error if one occurred. -func EnableWindowsANSIConsole() (uint32, error) { - handle, err := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) - if err != nil { - return 0, err - } - - var mode uint32 - err = windows.GetConsoleMode(handle, &mode) - if err != nil { - return 0, err - } - - // See https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences - if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING { - vtpmode := mode | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING - if err := windows.SetConsoleMode(handle, vtpmode); err != nil { - return 0, err - } - } - - return mode, nil -} - -// RestoreWindowsConsole restores the console mode to a previous state. -func RestoreWindowsConsole(mode uint32) error { - handle, err := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) - if err != nil { - return err - } - - return windows.SetConsoleMode(handle, mode) -} - -// EnableVirtualTerminalProcessing enables virtual terminal processing on -// Windows for o and returns a function that restores o to its previous state. -// On non-Windows platforms, or if o does not refer to a terminal, then it -// returns a non-nil no-op function and no error. -func EnableVirtualTerminalProcessing(o *Output) (restoreFunc func() error, err error) { - // There is nothing to restore until we set the console mode. - restoreFunc = func() error { - return nil - } - - // If o is not a tty, then there is nothing to do. - tty, ok := o.Writer().(*os.File) - if tty == nil || !ok { - return - } - - // Get the current console mode. If there is an error, assume that o is not - // a terminal, discard the error, and return. - var mode uint32 - if err2 := windows.GetConsoleMode(windows.Handle(tty.Fd()), &mode); err2 != nil { - return - } - - // If virtual terminal processing is already set, then there is nothing to - // do and nothing to restore. - if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING == windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING { - return - } - - // Enable virtual terminal processing. See - // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences - if err2 := windows.SetConsoleMode(windows.Handle(tty.Fd()), mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err2 != nil { - err = fmt.Errorf("windows.SetConsoleMode: %w", err2) - return - } - - // Set the restore function. We maintain a reference to the tty in the - // closure (rather than just its handle) to ensure that the tty is not - // closed by a finalizer. - restoreFunc = func() error { - return windows.SetConsoleMode(windows.Handle(tty.Fd()), mode) - } - - return -} diff --git a/vendor/github.com/nunnatsa/ginkgolinter/config/config.go b/vendor/github.com/nunnatsa/ginkgolinter/config/config.go index 1fee023c..e62158ed 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/config/config.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/config/config.go @@ -63,8 +63,8 @@ func (s *Config) UpdateFromComment(commentGroup []*ast.CommentGroup) { } for _, cmnt := range cmntList.List { - commentLines := strings.Split(cmnt.Text, "\n") - for _, comment := range commentLines { + commentLines := strings.SplitSeq(cmnt.Text, "\n") + for comment := range commentLines { comment = strings.TrimPrefix(comment, "//") comment = strings.TrimPrefix(comment, "/*") comment = strings.TrimSuffix(comment, "*/") diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actualarg.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actualarg.go index 735c03c8..3b49fe57 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actualarg.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/actual/actualarg.go @@ -32,6 +32,7 @@ const ( TBParamArgType MultiRetsArgType ErrorMethodArgType + ErrorMethodNoErrArgType ErrorTypeArgType @@ -51,10 +52,10 @@ func getActualArgPayload(actualExprClone *ast.CallExpr, pass *analysis.Pass, inf var arg ArgPayload - if info.HasErrorMethod { - arg = &ErrorMethodPayload{} - } else if value.IsExprError(pass, origArgExpr) { - arg = newErrPayload(origArgExpr, argExprClone, pass) + if value.IsExprError(pass, origArgExpr, info.HasErrorMethod) { + arg = newErrPayload(origArgExpr, argExprClone, pass, info.HasErrorMethod) + } else if info.HasErrorMethod { + arg = &ErrorMethodNoErrPayload{} } else { switch expr := origArgExpr.(type) { case *ast.CallExpr: @@ -179,9 +180,17 @@ type ErrPayload struct { value.Valuer } -func newErrPayload(orig, clone ast.Expr, pass *analysis.Pass) *ErrPayload { +func newErrPayload(orig, clone ast.Expr, pass *analysis.Pass, withErrorMethod bool) ArgPayload { + v := value.GetValuerWithError(orig, clone, pass, withErrorMethod) + + if withErrorMethod { + return &ErrorMethodPayload{ + Valuer: v, + } + } + return &ErrPayload{ - Valuer: value.GetValuer(orig, clone, pass), + Valuer: v, } } @@ -189,10 +198,18 @@ func (*ErrPayload) ArgType() ArgType { return ErrActualArgType | ErrorTypeArgType } -type ErrorMethodPayload struct{} +type ErrorMethodNoErrPayload struct{} + +func (ErrorMethodNoErrPayload) ArgType() ArgType { + return ErrorMethodNoErrArgType +} + +type ErrorMethodPayload struct { + value.Valuer +} func (ErrorMethodPayload) ArgType() ArgType { - return ErrorMethodArgType | ErrorTypeArgType + return ErrorMethodArgType | ErrActualArgType | ErrorTypeArgType } func parseBinaryExpr(origActualExpr, argExprClone *ast.BinaryExpr, pass *analysis.Pass) ArgPayload { diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/matcher/errormatchers.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/matcher/errormatchers.go index 9442ca32..18f2a0b6 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/matcher/errormatchers.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/matcher/errormatchers.go @@ -146,7 +146,7 @@ func (m *MatchErrorMatcherWithMatcher) NumArgs() int { func newMatchErrorMatcher(args []ast.Expr, pass *analysis.Pass) MatchErrorMatcher { numArgs := len(args) - if value.IsExprError(pass, args[0]) { + if value.IsExprError(pass, args[0], false) { return &MatchErrorMatcherWithErr{numArgs: numArgs} } diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/value/value.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/value/value.go index d615cbe7..1ecd793d 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/value/value.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/expression/value/value.go @@ -24,6 +24,10 @@ type Valuer interface { } func GetValuer(orig, clone ast.Expr, pass *analysis.Pass) Valuer { + return GetValuerWithError(orig, clone, pass, false) +} + +func GetValuerWithError(orig, clone ast.Expr, pass *analysis.Pass, withErrorMethod bool) Valuer { val := New(orig, clone, pass) unspecified := UnspecifiedValue{ Value: val, @@ -33,7 +37,7 @@ func GetValuer(orig, clone ast.Expr, pass *analysis.Pass) Valuer { return unspecified } - if IsExprError(pass, orig) { + if IsExprError(pass, orig, withErrorMethod) { return &ErrValue{ Value: val, err: clone, @@ -187,7 +191,7 @@ func Is[T any](x any) bool { return matchType } -func IsExprError(pass *analysis.Pass, expr ast.Expr) bool { +func IsExprError(pass *analysis.Pass, expr ast.Expr, withErrorMethod bool) bool { actualArgType := pass.TypesInfo.TypeOf(expr) switch t := actualArgType.(type) { case *gotypes.Named: @@ -204,9 +208,14 @@ func IsExprError(pass *analysis.Pass, expr ast.Expr) bool { case *gotypes.Tuple: if t.Len() > 0 { - switch t0 := t.At(0).Type().(type) { + retVal := t.At(0).Type() + if withErrorMethod { + retVal = t.At(t.Len() - 1).Type() + } + + switch retVal.(type) { case *gotypes.Named, *gotypes.Pointer: - if typecheck.ImplementsError(t0) { + if typecheck.ImplementsError(retVal) { return true } } diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/reports/report-builder.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/reports/report-builder.go index dee88bd2..0302df29 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/reports/report-builder.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/reports/report-builder.go @@ -97,6 +97,8 @@ func (b *Builder) getMessage() string { sb.WriteString(strings.Join(b.issues, "; ")) if b.suggestFix && len(b.fixOffer) != 0 { + //nolint:staticcheck + // staticcheck false positive sb.WriteString(fmt.Sprintf(". Consider using `%s` instead", b.fixOffer)) } diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/errorequalnilrule.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/errorequalnilrule.go index b9441ff2..aa03d307 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/errorequalnilrule.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/errorequalnilrule.go @@ -40,7 +40,7 @@ func (r ErrorEqualNilRule) Apply(gexp *expression.GomegaExpression, config confi return false } - if v, ok := gexp.GetActualArg().(value.Valuer); ok && v.IsFunc() || gexp.ActualArgTypeIs(actual.ErrFuncActualArgType) { + if v, ok := gexp.GetActualArg().(value.Valuer); ok && v.IsFunc() && !gexp.ActualArgTypeIs(actual.ErrorMethodArgType) || gexp.ActualArgTypeIs(actual.ErrFuncActualArgType) { gexp.SetMatcherSucceed() } else { gexp.ReverseAssertionFuncLogic() diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/haveoccurredrule.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/haveoccurredrule.go index f4dea489..f9d68c15 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/haveoccurredrule.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/haveoccurredrule.go @@ -45,11 +45,13 @@ func (r HaveOccurredRule) Apply(gexp *expression.GomegaExpression, config config return true } - if config.ForceSucceedForFuncs && gexp.GetActualArg().(*actual.ErrPayload).IsFunc() { - gexp.ReverseAssertionFuncLogic() - gexp.SetMatcherSucceed() - reportBuilder.AddIssue(true, "prefer using the Succeed matcher for error function, instead of HaveOccurred") - return true + if config.ForceSucceedForFuncs { + if errPayload, ok := gexp.GetActualArg().(*actual.ErrPayload); ok && errPayload.IsFunc() { + gexp.ReverseAssertionFuncLogic() + gexp.SetMatcherSucceed() + reportBuilder.AddIssue(true, "prefer using the Succeed matcher for error function, instead of HaveOccurred") + return true + } } return false diff --git a/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/succeedrule.go b/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/succeedrule.go index 93966986..f53beb57 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/succeedrule.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/internal/rules/succeedrule.go @@ -54,7 +54,7 @@ func (r SucceedRule) Apply(gexp *expression.GomegaExpression, config config.Conf } if !gexp.ActualArgTypeIs(actual.ErrorTypeArgType) { - if gexp.IsActualTuple() { + if gexp.IsActualTuple() && !gexp.ActualArgTypeIs(actual.ErrorMethodNoErrArgType) { reportBuilder.AddIssue(false, "the Success matcher does not support multiple values") } else { reportBuilder.AddIssue(false, "asserting a non-error type with Succeed matcher") @@ -62,13 +62,15 @@ func (r SucceedRule) Apply(gexp *expression.GomegaExpression, config config.Conf return true } - if config.ForceSucceedForFuncs && !gexp.GetActualArg().(*actual.ErrPayload).IsFunc() { - gexp.ReverseAssertionFuncLogic() - gexp.SetMatcherHaveOccurred() + if config.ForceSucceedForFuncs { + if errPayload, ok := gexp.GetActualArg().(*actual.ErrPayload); ok && !errPayload.IsFunc() { + gexp.ReverseAssertionFuncLogic() + gexp.SetMatcherHaveOccurred() - reportBuilder.AddIssue(true, "prefer using the HaveOccurred matcher for non-function error value, instead of Succeed") + reportBuilder.AddIssue(true, "prefer using the HaveOccurred matcher for non-function error value, instead of Succeed") - return true + return true + } } return false diff --git a/vendor/github.com/pelletier/go-toml/v2/.gitignore b/vendor/github.com/pelletier/go-toml/v2/.gitignore index 4b7c4eda..eaf580df 100644 --- a/vendor/github.com/pelletier/go-toml/v2/.gitignore +++ b/vendor/github.com/pelletier/go-toml/v2/.gitignore @@ -5,3 +5,4 @@ cmd/tomljson/tomljson cmd/tomltestgen/tomltestgen dist tests/ +test-results diff --git a/vendor/github.com/pelletier/go-toml/v2/.golangci.toml b/vendor/github.com/pelletier/go-toml/v2/.golangci.toml index 067db551..7d2e5b04 100644 --- a/vendor/github.com/pelletier/go-toml/v2/.golangci.toml +++ b/vendor/github.com/pelletier/go-toml/v2/.golangci.toml @@ -1,84 +1,76 @@ -[service] -golangci-lint-version = "1.39.0" - -[linters-settings.wsl] -allow-assign-and-anything = true - -[linters-settings.exhaustive] -default-signifies-exhaustive = true +version = "2" [linters] -disable-all = true +default = "none" enable = [ "asciicheck", "bodyclose", - "cyclop", - "deadcode", - "depguard", "dogsled", "dupl", "durationcheck", "errcheck", "errorlint", "exhaustive", - # "exhaustivestruct", - "exportloopref", "forbidigo", - # "forcetypeassert", - "funlen", - "gci", - # "gochecknoglobals", "gochecknoinits", - "gocognit", "goconst", "gocritic", - "gocyclo", - "godot", - "godox", - # "goerr113", - "gofmt", - "gofumpt", + "godoclint", "goheader", - "goimports", - "golint", - "gomnd", - # "gomoddirectives", "gomodguard", "goprintffuncname", "gosec", - "gosimple", "govet", - # "ifshort", "importas", "ineffassign", "lll", "makezero", + "mirror", "misspell", "nakedret", - "nestif", "nilerr", - # "nlreturn", "noctx", "nolintlint", - #"paralleltest", + "perfsprint", "prealloc", "predeclared", "revive", "rowserrcheck", "sqlclosecheck", "staticcheck", - "structcheck", - "stylecheck", - # "testpackage", "thelper", "tparallel", - "typecheck", "unconvert", "unparam", "unused", - "varcheck", + "usetesting", "wastedassign", "whitespace", - # "wrapcheck", - # "wsl" +] + +[linters.settings.exhaustive] +default-signifies-exhaustive = true + +[linters.settings.lll] +line-length = 150 + +[[linters.exclusions.rules]] +path = ".test.go" +linters = ["goconst", "gosec"] + +[[linters.exclusions.rules]] +path = "main.go" +linters = ["forbidigo"] + +[[linters.exclusions.rules]] +path = "internal" +linters = ["revive"] +text = "(exported|indent-error-flow): " + +[formatters] +enable = [ + "gci", + "gofmt", + "gofumpt", + "goimports", ] diff --git a/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml b/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml index 47f0f591..3208f721 100644 --- a/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml +++ b/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml @@ -22,7 +22,6 @@ builds: - linux_riscv64 - windows_amd64 - windows_arm64 - - windows_arm - darwin_amd64 - darwin_arm64 - id: tomljson @@ -42,7 +41,6 @@ builds: - linux_riscv64 - windows_amd64 - windows_arm64 - - windows_arm - darwin_amd64 - darwin_arm64 - id: jsontoml @@ -62,7 +60,6 @@ builds: - linux_arm - windows_amd64 - windows_arm64 - - windows_arm - darwin_amd64 - darwin_arm64 universal_binaries: @@ -77,39 +74,43 @@ universal_binaries: name_template: jsontoml archives: - id: jsontoml - format: tar.xz - builds: + formats: + - tar.xz + ids: - jsontoml files: - none* name_template: "{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}" - id: tomljson - format: tar.xz - builds: + formats: + - tar.xz + ids: - tomljson files: - none* name_template: "{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}" - id: tomll - format: tar.xz - builds: + formats: + - tar.xz + ids: - tomll files: - none* name_template: "{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}" -dockers: +dockers_v2: - id: tools - goos: linux - goarch: amd64 ids: - jsontoml - tomljson - tomll - image_templates: - - "ghcr.io/pelletier/go-toml:latest" - - "ghcr.io/pelletier/go-toml:{{ .Tag }}" - - "ghcr.io/pelletier/go-toml:v{{ .Major }}" - skip_push: false + images: + - "ghcr.io/pelletier/go-toml" + tags: + - "latest" + - "{{ .Tag }}" + - "v{{ .Major }}" + platforms: + - linux/amd64 checksum: name_template: 'sha256sums.txt' snapshot: diff --git a/vendor/github.com/pelletier/go-toml/v2/AGENTS.md b/vendor/github.com/pelletier/go-toml/v2/AGENTS.md new file mode 100644 index 00000000..f495afd8 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/AGENTS.md @@ -0,0 +1,73 @@ +# Agent Guidelines for go-toml + +This file provides guidelines for AI agents contributing to go-toml. All agents must follow these rules derived from [CONTRIBUTING.md](./CONTRIBUTING.md). + +## Project Overview + +go-toml is a TOML library for Go. The goal is to provide an easy-to-use and efficient TOML implementation that gets the job done without getting in the way. + +## Code Change Rules + +### Backward Compatibility + +- **No backward-incompatible changes** unless explicitly discussed and approved +- Avoid breaking people's programs unless absolutely necessary + +### Testing Requirements + +- **All bug fixes must include regression tests** +- **All new code must be tested** +- Run tests before submitting: `go test -race ./...` +- Test coverage must not decrease. Check with: + ```bash + go test -covermode=atomic -coverprofile=coverage.out + go tool cover -func=coverage.out + ``` +- All lines of code touched by changes should be covered by tests + +### Performance Requirements + +- go-toml aims to stay efficient; avoid performance regressions +- Run benchmarks to verify: `go test ./... -bench=. -count=10` +- Compare results using [benchstat](https://pkg.go.dev/golang.org/x/perf/cmd/benchstat) + +### Documentation + +- New features or feature extensions must include documentation +- Documentation lives in [README.md](./README.md) and throughout source code + +### Code Style + +- Follow existing code format and structure +- Code must pass `go fmt` +- Code must pass linting with the same golangci-lint version as CI (see version in `.github/workflows/lint.yml`): + ```bash + # Install specific version (check lint.yml for current version) + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin + # Run linter + golangci-lint run ./... + ``` + +### Commit Messages + +- Commit messages must explain **why** the change is needed +- Keep messages clear and informative even if details are in the PR description + +### Capabilities + +go-toml tracks system-level capabilities using [capslock](https://github.com/google/capslock). The baseline is in `capability_baseline.txt` and CI enforces that it does not grow. + +- **Do not introduce new capabilities.** PRs that increase the capability set (e.g., adding network access, subprocess execution, syscalls) are unlikely to be accepted. +- If a change causes the capabilities check to fail, do not update the baseline to make it pass. Instead, rethink the approach to avoid requiring new capabilities. +- To check locally: `./caps.sh check` (requires `capslock` installed via `go install github.com/google/capslock/cmd/capslock@latest`) + +## Pull Request Checklist + +Before submitting: + +1. Tests pass (`go test -race ./...`) +2. No backward-incompatible changes (unless discussed) +3. Relevant documentation added/updated +4. No performance regression (verify with benchmarks) +5. Capabilities are not increasing (`./caps.sh check`) +6. Title is clear and understandable for changelog diff --git a/vendor/github.com/pelletier/go-toml/v2/CONTRIBUTING.md b/vendor/github.com/pelletier/go-toml/v2/CONTRIBUTING.md index 96ecf9e2..65a3ff49 100644 --- a/vendor/github.com/pelletier/go-toml/v2/CONTRIBUTING.md +++ b/vendor/github.com/pelletier/go-toml/v2/CONTRIBUTING.md @@ -33,7 +33,7 @@ The documentation is present in the [README][readme] and thorough the source code. On release, it gets updated on [pkg.go.dev][pkg.go.dev]. To make a change to the documentation, create a pull request with your proposed changes. For simple changes like that, the easiest way to go is probably the "Fork this -project and edit the file" button on Github, displayed at the top right of the +project and edit the file" button on GitHub, displayed at the top right of the file. Unless it's a trivial change (for example a typo), provide a little bit of context in your pull request description or commit message. @@ -92,6 +92,48 @@ However, given GitHub's new policy to _not_ run Actions on pull requests until a maintainer clicks on button, it is highly recommended that you run them locally as you make changes. +### Test across Go versions + +The repository includes tooling to test go-toml across multiple Go versions +(1.11 through 1.25) both locally and in GitHub Actions. + +#### Local testing with Docker + +Prerequisites: Docker installed and running, Bash shell, `rsync` command. + +```bash +# Test all Go versions in parallel (default) +./test-go-versions.sh + +# Test specific versions +./test-go-versions.sh 1.21 1.22 1.23 + +# Test sequentially (slower but uses less resources) +./test-go-versions.sh --sequential + +# Verbose output with custom results directory +./test-go-versions.sh --verbose --output ./my-results 1.24 1.25 + +# Show all options +./test-go-versions.sh --help +``` + +The script creates Docker containers for each Go version and runs the full test +suite. Results are saved to a `test-results/` directory with individual logs and +a comprehensive summary report. + +The script only exits with a non-zero status code if either of the two most +recent Go versions fail. + +#### GitHub Actions testing (maintainers) + +1. Go to the **Actions** tab in the GitHub repository +2. Select **"Go Versions Compatibility Test"** from the workflow list +3. Click **"Run workflow"** +4. Optionally customize: + - **Go versions**: Space-separated list (e.g., `1.21 1.22 1.23`) + - **Execution mode**: Parallel (faster) or sequential (more stable) + ### Check coverage We use `go tool cover` to compute test coverage. Most code editors have a way to @@ -111,7 +153,7 @@ code lowers the coverage. Go-toml aims to stay efficient. We rely on a set of scenarios executed with Go's builtin benchmark systems. Because of their noisy nature, containers provided by -Github Actions cannot be reliably used for benchmarking. As a result, you are +GitHub Actions cannot be reliably used for benchmarking. As a result, you are responsible for checking that your changes do not incur a performance penalty. You can run their following to execute benchmarks: @@ -138,6 +180,25 @@ description. Pull requests that lower performance will receive more scrutiny. [benchstat]: https://pkg.go.dev/golang.org/x/perf/cmd/benchstat +### Capabilities + +We use [capslock](https://github.com/google/capslock) to track what +system-level capabilities (file access, network, syscalls, etc.) each package +requires. The current baseline is in `capability_baseline.txt`. CI will fail if +a change introduces a new capability. + +**Pull requests that increase the set of capabilities are unlikely to be +accepted.** go-toml is a parsing library and should not need network access, +subprocess execution, or other capabilities beyond what it already uses. + +If you believe a new capability is genuinely needed, discuss it in an issue +first. To update the baseline after approval: + +```bash +go install github.com/google/capslock/cmd/capslock@latest +./caps.sh generate +``` + ### Style Try to look around and follow the same format and structure as the rest of the @@ -168,13 +229,13 @@ Checklist: 1. Decide on the next version number. Use semver. Review commits since last version to assess. 2. Tag release. For example: -``` -git checkout v2 -git pull -git tag v2.2.0 -git push --tags -``` -3. CI automatically builds a draft Github release. Review it and edit as + ``` + git checkout v2 + git pull + git tag v2.2.0 + git push --tags + ``` +3. CI automatically builds a draft GitHub release. Review it and edit as necessary. Look for "Other changes". That would indicate a pull request not labeled properly. Tweak labels and pull request titles until changelog looks good for users. diff --git a/vendor/github.com/pelletier/go-toml/v2/Dockerfile b/vendor/github.com/pelletier/go-toml/v2/Dockerfile index b9e93323..ebd9baba 100644 --- a/vendor/github.com/pelletier/go-toml/v2/Dockerfile +++ b/vendor/github.com/pelletier/go-toml/v2/Dockerfile @@ -1,5 +1,6 @@ FROM scratch ENV PATH "$PATH:/bin" -COPY tomll /bin/tomll -COPY tomljson /bin/tomljson -COPY jsontoml /bin/jsontoml +ARG TARGETPLATFORM +COPY $TARGETPLATFORM/tomll /bin/tomll +COPY $TARGETPLATFORM/tomljson /bin/tomljson +COPY $TARGETPLATFORM/jsontoml /bin/jsontoml diff --git a/vendor/github.com/pelletier/go-toml/v2/README.md b/vendor/github.com/pelletier/go-toml/v2/README.md index 0755e556..067ba3fd 100644 --- a/vendor/github.com/pelletier/go-toml/v2/README.md +++ b/vendor/github.com/pelletier/go-toml/v2/README.md @@ -2,7 +2,7 @@ Go library for the [TOML](https://toml.io/en/) format. -This library supports [TOML v1.0.0](https://toml.io/en/v1.0.0). +This library supports [TOML v1.1.0](https://toml.io/en/v1.1.0). [🐞 Bug Reports](https://github.com/pelletier/go-toml/issues) @@ -21,8 +21,6 @@ documentation. import "github.com/pelletier/go-toml/v2" ``` -See [Modules](#Modules). - ## Features ### Stdlib behavior @@ -30,6 +28,11 @@ See [Modules](#Modules). As much as possible, this library is designed to behave similarly as the standard library's `encoding/json`. +When encoding structs, fields tagged with `omitempty` are omitted if they are +empty. For `time.Time`, the zero value is considered empty, so timestamps such +as `created_at` or `updated_at` are not written unless you remove `omitempty` +from the struct tag or use a pointer type (`*time.Time`). + ### Performance While go-toml favors usability, it is written with performance in mind. Most @@ -67,7 +70,7 @@ this use-case, go-toml provides [`LocalDate`][tld], [`LocalTime`][tlt], and making them convenient yet unambiguous structures for their respective TOML representation. -[ldt]: https://toml.io/en/v1.0.0#local-date-time +[ldt]: https://toml.io/en/v1.1.0#local-date-time [tld]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDate [tlt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalTime [tldt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDateTime @@ -107,7 +110,11 @@ type MyConfig struct { ### Unmarshaling [`Unmarshal`][unmarshal] reads a TOML document and fills a Go structure with its -content. For example: +content. + +Note that the struct variable names are _capitalized_, while the variables in the toml document are _lowercase_. + +For example: ```go doc := ` @@ -133,6 +140,62 @@ fmt.Println("tags:", cfg.Tags) [unmarshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Unmarshal + +Here is an example using tables with some simple nesting: + +```go +doc := ` +age = 45 +fruits = ["apple", "pear"] + +# these are very important! +[my-variables] +first = 1 +second = 0.2 +third = "abc" + +# this is not so important. +[my-variables.b] +bfirst = 123 +` + +var Document struct { + Age int + Fruits []string + + Myvariables struct { + First int + Second float64 + Third string + + B struct { + Bfirst int + } + } `toml:"my-variables"` +} + +err := toml.Unmarshal([]byte(doc), &Document) +if err != nil { + panic(err) +} + +fmt.Println("age:", Document.Age) +fmt.Println("fruits:", Document.Fruits) +fmt.Println("my-variables.first:", Document.Myvariables.First) +fmt.Println("my-variables.second:", Document.Myvariables.Second) +fmt.Println("my-variables.third:", Document.Myvariables.Third) +fmt.Println("my-variables.B.Bfirst:", Document.Myvariables.B.Bfirst) + +// Output: +// age: 45 +// fruits: [apple pear] +// my-variables.first: 1 +// my-variables.second: 0.2 +// my-variables.third: abc +// my-variables.B.Bfirst: 123 +``` + + ### Marshaling [`Marshal`][marshal] is the opposite of Unmarshal: it represents a Go structure @@ -175,17 +238,17 @@ the AST level. See https://pkg.go.dev/github.com/pelletier/go-toml/v2/unstable. Execution time speedup compared to other Go TOML libraries: - - - - - - - - - - - + + + + + + + + + + +
Benchmarkgo-toml v1BurntSushi/toml
Marshal/HugoFrontMatter-21.9x2.2x
Marshal/ReferenceFile/map-21.7x2.1x
Marshal/ReferenceFile/struct-22.2x3.0x
Unmarshal/HugoFrontMatter-22.9x2.7x
Unmarshal/ReferenceFile/map-22.6x2.7x
Unmarshal/ReferenceFile/struct-24.6x5.1x
Benchmarkgo-toml v1BurntSushi/toml
Marshal/HugoFrontMatter-22.3x2.4x
Marshal/ReferenceFile/map-22.2x2.6x
Marshal/ReferenceFile/struct-24.9x5.0x
Unmarshal/HugoFrontMatter-27.8x5.9x
Unmarshal/ReferenceFile/map-26.8x6.4x
Unmarshal/ReferenceFile/struct-26.8x6.3x
See more

The table above has the results of the most common use-cases. The table below @@ -193,40 +256,26 @@ contains the results of all benchmarks, including unrealistic ones. It is provided for completeness.

- - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
Benchmarkgo-toml v1BurntSushi/toml
Marshal/SimpleDocument/map-21.8x2.7x
Marshal/SimpleDocument/struct-22.7x3.8x
Unmarshal/SimpleDocument/map-23.8x3.0x
Unmarshal/SimpleDocument/struct-25.6x4.1x
UnmarshalDataset/example-23.0x3.2x
UnmarshalDataset/code-22.3x2.9x
UnmarshalDataset/twitter-22.6x2.7x
UnmarshalDataset/citm_catalog-22.2x2.3x
UnmarshalDataset/canada-21.8x1.5x
UnmarshalDataset/config-24.1x2.9x
geomean2.7x2.8x
Benchmarkgo-toml v1BurntSushi/toml
Marshal/SimpleDocument/map-22.1x3.1x
Marshal/SimpleDocument/struct-23.4x4.8x
Unmarshal/SimpleDocument/map-210.1x7.0x
Unmarshal/SimpleDocument/struct-212.4x8.0x
UnmarshalDataset/example-28.2x6.9x
UnmarshalDataset/code-27.5x8.3x
UnmarshalDataset/twitter-29.0x7.6x
UnmarshalDataset/citm_catalog-25.0x4.5x
UnmarshalDataset/canada-26.4x4.7x
UnmarshalDataset/config-210.2x6.1x
geomean5.8x5.3x

This table can be generated with ./ci.sh benchmark -a -html.

-## Modules - -go-toml uses Go's standard modules system. - -Installation instructions: - -- Go ≥ 1.16: Nothing to do. Use the import in your code. The `go` command deals - with it automatically. -- Go ≥ 1.13: `GO111MODULE=on go get github.com/pelletier/go-toml/v2`. - -In case of trouble: [Go Modules FAQ][mod-faq]. - -[mod-faq]: https://github.com/golang/go/wiki/Modules#why-does-installing-a-tool-via-go-get-fail-with-error-cannot-find-main-module - ## Tools Go-toml provides three handy command line tools: @@ -265,304 +314,6 @@ Multiple versions are available on [ghcr.io][docker]. [docker]: https://github.com/pelletier/go-toml/pkgs/container/go-toml -## Migrating from v1 - -This section describes the differences between v1 and v2, with some pointers on -how to get the original behavior when possible. - -### Decoding / Unmarshal - -#### Automatic field name guessing - -When unmarshaling to a struct, if a key in the TOML document does not exactly -match the name of a struct field or any of the `toml`-tagged field, v1 tries -multiple variations of the key ([code][v1-keys]). - -V2 instead does a case-insensitive matching, like `encoding/json`. - -This could impact you if you are relying on casing to differentiate two fields, -and one of them is a not using the `toml` struct tag. The recommended solution -is to be specific about tag names for those fields using the `toml` struct tag. - -[v1-keys]: https://github.com/pelletier/go-toml/blob/a2e52561804c6cd9392ebf0048ca64fe4af67a43/marshal.go#L775-L781 - -#### Ignore preexisting value in interface - -When decoding into a non-nil `interface{}`, go-toml v1 uses the type of the -element in the interface to decode the object. For example: - -```go -type inner struct { - B interface{} -} -type doc struct { - A interface{} -} - -d := doc{ - A: inner{ - B: "Before", - }, -} - -data := ` -[A] -B = "After" -` - -toml.Unmarshal([]byte(data), &d) -fmt.Printf("toml v1: %#v\n", d) - -// toml v1: main.doc{A:main.inner{B:"After"}} -``` - -In this case, field `A` is of type `interface{}`, containing a `inner` struct. -V1 sees that type and uses it when decoding the object. - -When decoding an object into an `interface{}`, V2 instead disregards whatever -value the `interface{}` may contain and replaces it with a -`map[string]interface{}`. With the same data structure as above, here is what -the result looks like: - -```go -toml.Unmarshal([]byte(data), &d) -fmt.Printf("toml v2: %#v\n", d) - -// toml v2: main.doc{A:map[string]interface {}{"B":"After"}} -``` - -This is to match `encoding/json`'s behavior. There is no way to make the v2 -decoder behave like v1. - -#### Values out of array bounds ignored - -When decoding into an array, v1 returns an error when the number of elements -contained in the doc is superior to the capacity of the array. For example: - -```go -type doc struct { - A [2]string -} -d := doc{} -err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d) -fmt.Println(err) - -// (1, 1): unmarshal: TOML array length (3) exceeds destination array length (2) -``` - -In the same situation, v2 ignores the last value: - -```go -err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d) -fmt.Println("err:", err, "d:", d) -// err: d: {[one two]} -``` - -This is to match `encoding/json`'s behavior. There is no way to make the v2 -decoder behave like v1. - -#### Support for `toml.Unmarshaler` has been dropped - -This method was not widely used, poorly defined, and added a lot of complexity. -A similar effect can be achieved by implementing the `encoding.TextUnmarshaler` -interface and use strings. - -#### Support for `default` struct tag has been dropped - -This feature adds complexity and a poorly defined API for an effect that can be -accomplished outside of the library. - -It does not seem like other format parsers in Go support that feature (the -project referenced in the original ticket #202 has not been updated since 2017). -Given that go-toml v2 should not touch values not in the document, the same -effect can be achieved by pre-filling the struct with defaults (libraries like -[go-defaults][go-defaults] can help). Also, string representation is not well -defined for all types: it creates issues like #278. - -The recommended replacement is pre-filling the struct before unmarshaling. - -[go-defaults]: https://github.com/mcuadros/go-defaults - -#### `toml.Tree` replacement - -This structure was the initial attempt at providing a document model for -go-toml. It allows manipulating the structure of any document, encoding and -decoding from their TOML representation. While a more robust feature was -initially planned in go-toml v2, this has been ultimately [removed from -scope][nodoc] of this library, with no plan to add it back at the moment. The -closest equivalent at the moment would be to unmarshal into an `interface{}` and -use type assertions and/or reflection to manipulate the arbitrary -structure. However this would fall short of providing all of the TOML features -such as adding comments and be specific about whitespace. - - -#### `toml.Position` are not retrievable anymore - -The API for retrieving the position (line, column) of a specific TOML element do -not exist anymore. This was done to minimize the amount of concepts introduced -by the library (query path), and avoid the performance hit related to storing -positions in the absence of a document model, for a feature that seemed to have -little use. Errors however have gained more detailed position -information. Position retrieval seems better fitted for a document model, which -has been [removed from the scope][nodoc] of go-toml v2 at the moment. - -### Encoding / Marshal - -#### Default struct fields order - -V1 emits struct fields order alphabetically by default. V2 struct fields are -emitted in order they are defined. For example: - -```go -type S struct { - B string - A string -} - -data := S{ - B: "B", - A: "A", -} - -b, _ := tomlv1.Marshal(data) -fmt.Println("v1:\n" + string(b)) - -b, _ = tomlv2.Marshal(data) -fmt.Println("v2:\n" + string(b)) - -// Output: -// v1: -// A = "A" -// B = "B" - -// v2: -// B = 'B' -// A = 'A' -``` - -There is no way to make v2 encoder behave like v1. A workaround could be to -manually sort the fields alphabetically in the struct definition, or generate -struct types using `reflect.StructOf`. - -#### No indentation by default - -V1 automatically indents content of tables by default. V2 does not. However the -same behavior can be obtained using [`Encoder.SetIndentTables`][sit]. For example: - -```go -data := map[string]interface{}{ - "table": map[string]string{ - "key": "value", - }, -} - -b, _ := tomlv1.Marshal(data) -fmt.Println("v1:\n" + string(b)) - -b, _ = tomlv2.Marshal(data) -fmt.Println("v2:\n" + string(b)) - -buf := bytes.Buffer{} -enc := tomlv2.NewEncoder(&buf) -enc.SetIndentTables(true) -enc.Encode(data) -fmt.Println("v2 Encoder:\n" + string(buf.Bytes())) - -// Output: -// v1: -// -// [table] -// key = "value" -// -// v2: -// [table] -// key = 'value' -// -// -// v2 Encoder: -// [table] -// key = 'value' -``` - -[sit]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Encoder.SetIndentTables - -#### Keys and strings are single quoted - -V1 always uses double quotes (`"`) around strings and keys that cannot be -represented bare (unquoted). V2 uses single quotes instead by default (`'`), -unless a character cannot be represented, then falls back to double quotes. As a -result of this change, `Encoder.QuoteMapKeys` has been removed, as it is not -useful anymore. - -There is no way to make v2 encoder behave like v1. - -#### `TextMarshaler` emits as a string, not TOML - -Types that implement [`encoding.TextMarshaler`][tm] can emit arbitrary TOML in -v1. The encoder would append the result to the output directly. In v2 the result -is wrapped in a string. As a result, this interface cannot be implemented by the -root object. - -There is no way to make v2 encoder behave like v1. - -[tm]: https://golang.org/pkg/encoding/#TextMarshaler - -#### `Encoder.CompactComments` has been removed - -Emitting compact comments is now the default behavior of go-toml. This option -is not necessary anymore. - -#### Struct tags have been merged - -V1 used to provide multiple struct tags: `comment`, `commented`, `multiline`, -`toml`, and `omitempty`. To behave more like the standard library, v2 has merged -`toml`, `multiline`, `commented`, and `omitempty`. For example: - -```go -type doc struct { - // v1 - F string `toml:"field" multiline:"true" omitempty:"true" commented:"true"` - // v2 - F string `toml:"field,multiline,omitempty,commented"` -} -``` - -Has a result, the `Encoder.SetTag*` methods have been removed, as there is just -one tag now. - -#### `Encoder.ArraysWithOneElementPerLine` has been renamed - -The new name is `Encoder.SetArraysMultiline`. The behavior should be the same. - -#### `Encoder.Indentation` has been renamed - -The new name is `Encoder.SetIndentSymbol`. The behavior should be the same. - - -#### Embedded structs behave like stdlib - -V1 defaults to merging embedded struct fields into the embedding struct. This -behavior was unexpected because it does not follow the standard library. To -avoid breaking backward compatibility, the `Encoder.PromoteAnonymous` method was -added to make the encoder behave correctly. Given backward compatibility is not -a problem anymore, v2 does the right thing by default: it follows the behavior -of `encoding/json`. `Encoder.PromoteAnonymous` has been removed. - -[nodoc]: https://github.com/pelletier/go-toml/discussions/506#discussioncomment-1526038 - -### `query` - -go-toml v1 provided the [`go-toml/query`][query] package. It allowed to run -JSONPath-style queries on TOML files. This feature is not available in v2. For a -replacement, check out [dasel][dasel]. - -This package has been removed because it was essentially not supported anymore -(last commit May 2020), increased the complexity of the code base, and more -complete solutions exist out there. - -[query]: https://github.com/pelletier/go-toml/tree/f99d6bbca119636aeafcf351ee52b3d202782627/query -[dasel]: https://github.com/TomWright/dasel - ## Versioning Expect for parts explicitly marked otherwise, go-toml follows [Semantic diff --git a/vendor/github.com/pelletier/go-toml/v2/capability_baseline.txt b/vendor/github.com/pelletier/go-toml/v2/capability_baseline.txt new file mode 100644 index 00000000..c556456c --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/capability_baseline.txt @@ -0,0 +1 @@ +github.com/pelletier/go-toml/v2: CAPABILITY_REFLECT, CAPABILITY_UNANALYZED, CAPABILITY_UNSAFE_POINTER diff --git a/vendor/github.com/pelletier/go-toml/v2/caps.sh b/vendor/github.com/pelletier/go-toml/v2/caps.sh new file mode 100644 index 00000000..31fec2b9 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/caps.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# +# Generates or checks the capability baseline for go-toml. +# +# Usage: +# ./caps.sh generate # regenerate capability_baseline.txt +# ./caps.sh check # check that capabilities haven't grown +# +# Requires: go, capslock (go install github.com/google/capslock/cmd/capslock@latest) + +set -euo pipefail + +BASELINE="capability_baseline.txt" +CAPSLOCK="${CAPSLOCK:-capslock}" + +# Capabilities that must never appear in any package. +FORBIDDEN_CAPS=( + CAPABILITY_NETWORK + CAPABILITY_CGO + CAPABILITY_EXEC +) + +capslock_to_baseline() { + "$CAPSLOCK" -packages=. -output=package -granularity=package \ + | jq -r 'to_entries | sort_by(.key) | .[] | .key + ": " + (.value | sort | join(", "))' +} + +generate() { + capslock_to_baseline > "$BASELINE" + echo "Wrote $BASELINE" +} + +check() { + if [ ! -f "$BASELINE" ]; then + echo "ERROR: $BASELINE not found. Run '$0 generate' first." + exit 1 + fi + + current=$(mktemp) + trap 'rm -f "$current"' EXIT + + capslock_to_baseline > "$current" + + failed=0 + + # Check for forbidden capabilities in current output. + for cap in "${FORBIDDEN_CAPS[@]}"; do + if grep -q "$cap" "$current"; then + echo "FORBIDDEN capability found: $cap" + grep "$cap" "$current" + failed=1 + fi + done + + # Extract all unique capability names from baseline and current. + baseline_caps=$(grep -oE 'CAPABILITY_[A-Z_]+' "$BASELINE" | sort -u) + current_caps=$(grep -oE 'CAPABILITY_[A-Z_]+' "$current" | sort -u) + + # Check for new capability names not in the baseline. + new_caps=$(comm -13 <(echo "$baseline_caps") <(echo "$current_caps")) + if [ -n "$new_caps" ]; then + echo "NEW capabilities detected (not in baseline):" + echo "$new_caps" + failed=1 + fi + + # Check for new per-package capabilities (a package gained a capability it didn't have before). + while IFS=': ' read -r pkg caps; do + baseline_pkg_caps=$(grep "^${pkg}:" "$BASELINE" 2>/dev/null | sed 's/^[^:]*: //' || true) + if [ -z "$baseline_pkg_caps" ]; then + echo "NEW package with capabilities: $pkg: $caps" + failed=1 + continue + fi + # Check each capability in current for this package + for cap in $(echo "$caps" | tr ', ' '\n' | grep -v '^$'); do + if ! echo "$baseline_pkg_caps" | grep -q "$cap"; then + echo "NEW capability for $pkg: $cap" + failed=1 + fi + done + done < "$current" + + if [ "$failed" -eq 1 ]; then + echo "" + echo "FAILED: capabilities have grown." + echo "If this is intentional, run '$0 generate' and commit the updated $BASELINE." + exit 1 + fi + + echo "OK: no new capabilities detected." +} + +case "${1:-}" in + generate) generate ;; + check) check ;; + *) + echo "Usage: $0 {generate|check}" + exit 1 + ;; +esac diff --git a/vendor/github.com/pelletier/go-toml/v2/ci.sh b/vendor/github.com/pelletier/go-toml/v2/ci.sh index 86217a9b..6b9723bf 100644 --- a/vendor/github.com/pelletier/go-toml/v2/ci.sh +++ b/vendor/github.com/pelletier/go-toml/v2/ci.sh @@ -146,13 +146,17 @@ bench() { pushd "$dir" + tags="" if [ "${replace}" != "" ]; then - find ./benchmark/ -iname '*.go' -exec sed -i -E "s|github.com/pelletier/go-toml/v2|${replace}|g" {} \; + find ./benchmark/ -iname '*.go' -exec sed -i -E "s|github.com/pelletier/go-toml/v2\"|${replace}\"|g" {} \; go get "${replace}" + # The realworld benchmarks use v2-only API and cannot compile against + # the other libraries; exclude them from cross-library comparisons. + tags="-tags cross_library_benchmark" fi export GOMAXPROCS=2 - go test '-bench=^Benchmark(Un)?[mM]arshal' -count=10 -run=Nothing ./... | tee "${out}" + go test ${tags} '-bench=^Benchmark(Un)?[mM]arshal' -count=10 -run=Nothing ./... | tee "${out}" popd if [ "${branch}" != "HEAD" ]; then @@ -195,6 +199,11 @@ for line in reversed(lines[2:]): "%.1fx" % (float(line[3])/v2), # v1 "%.1fx" % (float(line[7])/v2), # bs ]) + +if not results: + print("No benchmark results to display.", file=sys.stderr) + sys.exit(1) + # move geomean to the end results.append(results[0]) del results[0] diff --git a/vendor/github.com/pelletier/go-toml/v2/decode.go b/vendor/github.com/pelletier/go-toml/v2/decode.go index f0ec3b17..46de2fb4 100644 --- a/vendor/github.com/pelletier/go-toml/v2/decode.go +++ b/vendor/github.com/pelletier/go-toml/v2/decode.go @@ -1,6 +1,7 @@ package toml import ( + "bytes" "fmt" "math" "strconv" @@ -22,191 +23,340 @@ func parseInteger(b []byte) (int64, error) { panic(fmt.Errorf("invalid base '%c', should have been checked by scanIntOrFloat", b[1])) } } - return parseIntDec(b) } -func parseLocalDate(b []byte) (LocalDate, error) { - // full-date = date-fullyear "-" date-month "-" date-mday - // date-fullyear = 4DIGIT - // date-month = 2DIGIT ; 01-12 - // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year - var date LocalDate - - if len(b) != 10 || b[4] != '-' || b[7] != '-' { - return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD") +func parseIntHex(b []byte) (int64, error) { + var v uint64 + for _, c := range b[2:] { + if c == '_' { + continue + } + var d byte + switch { + case c >= '0' && c <= '9': + d = c - '0' + case c >= 'a' && c <= 'f': + d = c - 'a' + 10 + case c >= 'A' && c <= 'F': + d = c - 'A' + 10 + } + if v > math.MaxInt64>>4 { + return 0, unstable.NewParserError(b, "hexadecimal number is too large to fit in a 64-bit signed integer") + } + v = v<<4 | uint64(d) } + return int64(v), nil +} - var err error - - date.Year, err = parseDecimalDigits(b[0:4]) - if err != nil { - return LocalDate{}, err +func parseIntOct(b []byte) (int64, error) { + var v uint64 + for _, c := range b[2:] { + if c == '_' { + continue + } + if v > math.MaxInt64>>3 { + return 0, unstable.NewParserError(b, "octal number is too large to fit in a 64-bit signed integer") + } + v = v<<3 | uint64(c-'0') } + return int64(v), nil +} - date.Month, err = parseDecimalDigits(b[5:7]) - if err != nil { - return LocalDate{}, err +func parseIntBin(b []byte) (int64, error) { + var v uint64 + for _, c := range b[2:] { + if c == '_' { + continue + } + if v > math.MaxInt64>>1 { + return 0, unstable.NewParserError(b, "binary number is too large to fit in a 64-bit signed integer") + } + v = v<<1 | uint64(c-'0') } + return int64(v), nil +} - date.Day, err = parseDecimalDigits(b[8:10]) - if err != nil { - return LocalDate{}, err +func parseIntDec(b []byte) (int64, error) { + i := 0 + neg := false + switch b[0] { + case '-': + neg = true + i++ + case '+': + i++ } - if !isValidDate(date.Year, date.Month, date.Day) { - return LocalDate{}, unstable.NewParserError(b, "impossible date") + var limit uint64 = math.MaxInt64 + if neg { + limit = math.MaxInt64 + 1 } - return date, nil + var v uint64 + for ; i < len(b); i++ { + c := b[i] + if c == '_' { + continue + } + if v > limit/10 { + return 0, unstable.NewParserError(b, "decimal number is too large to fit in a 64-bit signed integer") + } + v = v*10 + uint64(c-'0') + if v > limit { + return 0, unstable.NewParserError(b, "decimal number is too large to fit in a 64-bit signed integer") + } + } + if neg { + return -int64(v), nil //nolint:gosec // v <= MaxInt64+1, the conversion wraps to the intended negative value + } + return int64(v), nil //nolint:gosec // v <= MaxInt64 } -func parseDecimalDigits(b []byte) (int, error) { - v := 0 - - for i, c := range b { - if c < '0' || c > '9' { - return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)") +func parseFloat(b []byte) (float64, error) { + i := 0 + if len(b) > 0 && (b[0] == '+' || b[0] == '-') { + i = 1 + } + if len(b) == i+3 { + switch b[i] { + case 'i': + // inf + if b[0] == '-' { + return math.Inf(-1), nil + } + return math.Inf(1), nil + case 'n': + // nan + return math.NaN(), nil } - v *= 10 - v += int(c - '0') } - return v, nil -} - -func parseDateTime(b []byte) (time.Time, error) { - // offset-date-time = full-date time-delim full-time - // full-time = partial-time time-offset - // time-offset = "Z" / time-numoffset - // time-numoffset = ( "+" / "-" ) time-hour ":" time-minute + // Fast path: a plain decimal whose significand fits in 53 bits and whose + // base-10 exponent is within [-22, 22] is parsed exactly with a single + // rounding (Clinger's method) straight from the bytes, with no string + // allocation and no full strconv parse. This is the common shape for + // numeric data (e.g. coordinate lists). Anything outside those bounds, or + // with underscores, falls through to strconv, which is the reference. + if f, ok := fastParseFloat(b); ok { + return f, nil + } + + // strconv.ParseFloat is the reference implementation for parsing + // floating point numbers. The position of underscores has already been + // validated by the parser; strip them so that they do not interfere with + // Go's own underscore rules. + cleaned := b + if bytes.IndexByte(b, '_') >= 0 { + cleaned = make([]byte, 0, len(b)) + for _, c := range b { + if c != '_' { + cleaned = append(cleaned, c) + } + } + } - dt, b, err := parseLocalDateTime(b) + f, err := strconv.ParseFloat(string(cleaned), 64) if err != nil { - return time.Time{}, err + return 0, unstable.NewParserError(b, "unable to parse float: %s", err) } + return f, nil +} - var zone *time.Location +// float64pow10 holds the powers of ten that are exactly representable as a +// float64 (10^0 .. 10^22). +var float64pow10 = [...]float64{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, + 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, +} - if len(b) == 0 { - // parser should have checked that when assigning the date time node - panic("date time should have a timezone") +// fastParseFloat parses b as a float64 using Clinger's exact method and reports +// whether it applied. It accepts only plain decimal numbers (optional sign, +// digits, one optional '.', optional 'e'/'E' exponent) whose significand fits +// in 53 bits and whose effective base-10 exponent is within [-22, 22]; under +// those conditions float64(significand) * 10^exp (or / 10^-exp) is the exact, +// correctly-rounded result, identical to strconv.ParseFloat. It returns +// ok=false (deferring to strconv) for underscores, hexadecimal floats, large +// significands or exponents, and any other shape. +func fastParseFloat(b []byte) (float64, bool) { + i := 0 + neg := false + if i < len(b) && (b[i] == '+' || b[i] == '-') { + neg = b[i] == '-' + i++ } - if b[0] == 'Z' || b[0] == 'z' { - b = b[1:] - zone = time.UTC - } else { - const dateTimeByteLen = 6 - if len(b) != dateTimeByteLen { - return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone") - } - var direction int - switch b[0] { - case '-': - direction = -1 - case '+': - direction = +1 + var mantissa uint64 + digits := 0 + fracDigits := 0 + sawDot := false + sawDigit := false + for ; i < len(b); i++ { + c := b[i] + switch { + case c >= '0' && c <= '9': + if digits >= 19 { + // Too many significant digits to accumulate without risking a + // uint64 overflow (and well past the 53-bit exact range). + return 0, false + } + mantissa = mantissa*10 + uint64(c-'0') + digits++ + if sawDot { + fracDigits++ + } + sawDigit = true + case c == '.': + if sawDot { + return 0, false + } + sawDot = true default: - return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character") - } - - if b[3] != ':' { - return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator") + goto exponent } - - hours, err := parseDecimalDigits(b[1:3]) - if err != nil { - return time.Time{}, err - } - if hours > 23 { - return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset hours") - } - - minutes, err := parseDecimalDigits(b[4:6]) - if err != nil { - return time.Time{}, err + } +exponent: + if !sawDigit { + return 0, false + } + exp := -fracDigits + if i < len(b) && (b[i] == 'e' || b[i] == 'E') { + i++ + esign := 1 + if i < len(b) && (b[i] == '+' || b[i] == '-') { + if b[i] == '-' { + esign = -1 + } + i++ } - if minutes > 59 { - return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset minutes") + if i >= len(b) { + return 0, false } - - seconds := direction * (hours*3600 + minutes*60) - if seconds == 0 { - zone = time.UTC - } else { - zone = time.FixedZone("", seconds) + eval := 0 + for ; i < len(b); i++ { + c := b[i] + if c < '0' || c > '9' { + return 0, false + } + eval = eval*10 + int(c-'0') + if eval > 1000 { + return 0, false + } } - b = b[dateTimeByteLen:] + exp += esign * eval } - - if len(b) > 0 { - return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone") + if i != len(b) { + // Trailing bytes (an underscore, a hexadecimal marker, ...). + return 0, false + } + if mantissa > 1<<53 { + return 0, false } - t := time.Date( - dt.Year, - time.Month(dt.Month), - dt.Day, - dt.Hour, - dt.Minute, - dt.Second, - dt.Nanosecond, - zone) + f := float64(mantissa) + switch { + case exp == 0: + case exp > 0 && exp <= 22: + f *= float64pow10[exp] + case exp < 0 && exp >= -22: + f /= float64pow10[-exp] + default: + return 0, false + } + if neg { + f = -f + } + return f, true +} - return t, nil +func isDecimalDigit(c byte) bool { + return c >= '0' && c <= '9' } -func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { - var dt LocalDateTime +// parseLocalDate parses a date of the exact form YYYY-MM-DD and validates +// its components. +func parseLocalDate(b []byte) (LocalDate, error) { + var date LocalDate - const localDateTimeByteMinLen = 11 - if len(b) < localDateTimeByteMinLen { - return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]") + if len(b) != 10 || b[4] != '-' || b[7] != '-' { + return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD") } - date, err := parseLocalDate(b[:10]) + var err error + date.Year, err = parseDecimalDigits(b[0:4]) if err != nil { - return dt, nil, err + return date, err + } + date.Month, err = parseDecimalDigits(b[5:7]) + if err != nil { + return date, err + } + date.Day, err = parseDecimalDigits(b[8:10]) + if err != nil { + return date, err } - dt.LocalDate = date - sep := b[10] - if sep != 'T' && sep != ' ' && sep != 't' { - return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space") + if date.Month < 1 || date.Month > 12 { + return date, unstable.NewParserError(b[5:7], "impossible date") + } + maxDay := daysIn(date.Month, date.Year) + if date.Day < 1 || date.Day > maxDay { + return date, unstable.NewParserError(b[8:10], "impossible date") } - t, rest, err := parseLocalTime(b[11:]) - if err != nil { - return dt, nil, err + return date, nil +} + +func daysIn(month int, year int) int { + switch month { + case 2: + if isLeapYear(year) { + return 29 + } + return 28 + case 4, 6, 9, 11: + return 30 + default: + return 31 } - dt.LocalTime = t +} - return dt, rest, nil +func isLeapYear(year int) bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) +} + +// parseDecimalDigits parses a sequence of digits as a decimal number. +func parseDecimalDigits(b []byte) (int, error) { + v := 0 + for i, c := range b { + if !isDecimalDigit(c) { + return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)") + } + v = v*10 + int(c-'0') + } + return v, nil } -// parseLocalTime is a bit different because it also returns the remaining -// []byte that is didn't need. This is to allow parseDateTime to parse those -// remaining bytes as a timezone. +// parseLocalTime parses a time of the form HH:MM with optional seconds and an +// optional fractional part (TOML v1.1.0). It returns the remaining bytes after +// the time. func parseLocalTime(b []byte) (LocalTime, []byte, error) { var ( nspow = [10]int{0, 1e8, 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0} t LocalTime ) - // check if b matches to have expected format HH:MM:SS[.NNNNNN] - const localTimeByteLen = 8 - if len(b) < localTimeByteLen { - return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]") + // check if b matches to have expected format HH:MM[:SS[.NNNNNN]] + const localTimeByteMinLen = 5 + if len(b) < localTimeByteMinLen { + return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM[:SS[.NNNNNN]]") } var err error - t.Hour, err = parseDecimalDigits(b[0:2]) if err != nil { return t, nil, err } - if t.Hour > 23 { return t, nil, unstable.NewParserError(b[0:2], "hour cannot be greater 23") } @@ -221,54 +371,53 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { if t.Minute > 59 { return t, nil, unstable.NewParserError(b[3:5], "minutes cannot be greater 59") } - if b[5] != ':' { - return t, nil, unstable.NewParserError(b[5:6], "expecting colon between minutes and seconds") - } - t.Second, err = parseDecimalDigits(b[6:8]) - if err != nil { - return t, nil, err - } + b = b[5:] - if t.Second > 60 { - return t, nil, unstable.NewParserError(b[6:8], "seconds cannot be greater 60") - } + // Seconds are optional (TOML v1.1.0). Fractional seconds may only appear + // when seconds are present: + // partial-time = time-hour ":" time-minute [ ":" time-second [ time-secfrac ] ] + secondsPresent := false + + if len(b) >= 1 && b[0] == ':' { + if len(b) < 3 { + return t, nil, unstable.NewParserError(b, "incomplete seconds") + } + + t.Second, err = parseDecimalDigits(b[1:3]) + if err != nil { + return t, nil, err + } + + if t.Second > 59 { + return t, nil, unstable.NewParserError(b[1:3], "seconds cannot be greater than 59") + } - b = b[8:] + b = b[3:] + secondsPresent = true + } - if len(b) >= 1 && b[0] == '.' { + if secondsPresent && len(b) >= 1 && b[0] == '.' { frac := 0 precision := 0 digits := 0 for i, c := range b[1:] { - if !isDigit(c) { + if !isDecimalDigit(c) { if i == 0 { return t, nil, unstable.NewParserError(b[0:1], "need at least one digit after fraction point") } break } digits++ - - const maxFracPrecision = 9 - if i >= maxFracPrecision { - // go-toml allows decoding fractional seconds - // beyond the supported precision of 9 - // digits. It truncates the fractional component - // to the supported precision and ignores the - // remaining digits. - // - // https://github.com/pelletier/go-toml/discussions/707 - continue + if i < 9 { + frac = frac*10 + int(c-'0') + precision++ } - - frac *= 10 - frac += int(c - '0') - precision++ } - if precision == 0 { - return t, nil, unstable.NewParserError(b[:1], "nanoseconds need at least one digit") + if digits == 0 { + return t, nil, unstable.NewParserError(b[0:1], "need at least one digit after fraction point") } t.Nanosecond = frac * nspow[precision] @@ -279,272 +428,111 @@ func parseLocalTime(b []byte) (LocalTime, []byte, error) { return t, b, nil } -//nolint:cyclop -func parseFloat(b []byte) (float64, error) { - if len(b) == 4 && (b[0] == '+' || b[0] == '-') && b[1] == 'n' && b[2] == 'a' && b[3] == 'n' { - return math.NaN(), nil - } - - cleaned, err := checkAndRemoveUnderscoresFloats(b) - if err != nil { - return 0, err - } - - if cleaned[0] == '.' { - return 0, unstable.NewParserError(b, "float cannot start with a dot") - } - - if cleaned[len(cleaned)-1] == '.' { - return 0, unstable.NewParserError(b, "float cannot end with a dot") - } - - dotAlreadySeen := false - for i, c := range cleaned { - if c == '.' { - if dotAlreadySeen { - return 0, unstable.NewParserError(b[i:i+1], "float can have at most one decimal point") - } - if !isDigit(cleaned[i-1]) { - return 0, unstable.NewParserError(b[i-1:i+1], "float decimal point must be preceded by a digit") - } - if !isDigit(cleaned[i+1]) { - return 0, unstable.NewParserError(b[i:i+2], "float decimal point must be followed by a digit") - } - dotAlreadySeen = true - } - } - - start := 0 - if cleaned[0] == '+' || cleaned[0] == '-' { - start = 1 - } - if cleaned[start] == '0' && len(cleaned) > start+1 && isDigit(cleaned[start+1]) { - return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes") - } - - f, err := strconv.ParseFloat(string(cleaned), 64) - if err != nil { - return 0, unstable.NewParserError(b, "unable to parse float: %w", err) - } - - return f, nil -} - -func parseIntHex(b []byte) (int64, error) { - cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:]) - if err != nil { - return 0, err - } - - i, err := strconv.ParseInt(string(cleaned), 16, 64) - if err != nil { - return 0, unstable.NewParserError(b, "couldn't parse hexadecimal number: %w", err) - } - - return i, nil -} +// parseLocalDateTime parses a local date time of the form +// YYYY-MM-DD(T| )HH:MM:SS[.NNNNNN]. It returns the remaining bytes after the +// date-time. +func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { + var dt LocalDateTime -func parseIntOct(b []byte) (int64, error) { - cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:]) - if err != nil { - return 0, err + const localDateTimeByteMinLen = 11 + if len(b) < localDateTimeByteMinLen { + return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM[:SS[.NNNNNNNNN]]") } - i, err := strconv.ParseInt(string(cleaned), 8, 64) + date, err := parseLocalDate(b[:10]) if err != nil { - return 0, unstable.NewParserError(b, "couldn't parse octal number: %w", err) + return dt, nil, err } + dt.LocalDate = date - return i, nil -} - -func parseIntBin(b []byte) (int64, error) { - cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:]) - if err != nil { - return 0, err + sep := b[10] + if sep != 'T' && sep != ' ' && sep != 't' { + return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space") } - i, err := strconv.ParseInt(string(cleaned), 2, 64) + t, rest, err := parseLocalTime(b[11:]) if err != nil { - return 0, unstable.NewParserError(b, "couldn't parse binary number: %w", err) + return dt, nil, err } + dt.LocalTime = t - return i, nil -} - -func isSign(b byte) bool { - return b == '+' || b == '-' + return dt, rest, nil } -func parseIntDec(b []byte) (int64, error) { - cleaned, err := checkAndRemoveUnderscoresIntegers(b) - if err != nil { - return 0, err - } - - startIdx := 0 - - if isSign(cleaned[0]) { - startIdx++ - } - - if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' { - return 0, unstable.NewParserError(b, "leading zero not allowed on decimal number") - } - - i, err := strconv.ParseInt(string(cleaned), 10, 64) +// parseDateTime parses a date-time with a timezone offset (Z or +/-HH:MM). +func parseDateTime(b []byte) (time.Time, error) { + dt, b, err := parseLocalDateTime(b) if err != nil { - return 0, unstable.NewParserError(b, "couldn't parse decimal number: %w", err) - } - - return i, nil -} - -func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) { - start := 0 - if b[start] == '+' || b[start] == '-' { - start++ - } - - if len(b) == start { - return b, nil + return time.Time{}, err } - if b[start] == '_' { - return nil, unstable.NewParserError(b[start:start+1], "number cannot start with underscore") - } + var zone *time.Location - if b[len(b)-1] == '_' { - return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") + if len(b) == 0 { + // parser should have checked that there is a timezone + return time.Time{}, unstable.NewParserError(b, "date-time is missing timezone") } - // fast path - i := 0 - for ; i < len(b); i++ { - if b[i] == '_' { - break + if b[0] == 'Z' || b[0] == 'z' { + b = b[1:] + zone = time.UTC + } else { + const dateTimeByteLen = 6 + if len(b) != dateTimeByteLen { + return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone") } - } - if i == len(b) { - return b, nil - } - - before := false - cleaned := make([]byte, i, len(b)) - copy(cleaned, b) - - for i++; i < len(b); i++ { - c := b[i] - if c == '_' { - if !before { - return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") - } - before = false - } else { - before = true - cleaned = append(cleaned, c) + var direction int + switch b[0] { + case '-': + direction = -1 + case '+': + direction = +1 + default: + return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character") } - } - - return cleaned, nil -} - -func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { - if b[0] == '_' { - return nil, unstable.NewParserError(b[0:1], "number cannot start with underscore") - } - if b[len(b)-1] == '_' { - return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") - } - - // fast path - i := 0 - for ; i < len(b); i++ { - if b[i] == '_' { - break + if b[3] != ':' { + return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator") } - } - if i == len(b) { - return b, nil - } - before := false - cleaned := make([]byte, 0, len(b)) + hours, err := parseDecimalDigits(b[1:3]) + if err != nil { + return time.Time{}, err + } + if hours > 23 { + return time.Time{}, unstable.NewParserError(b[1:3], "invalid timezone offset hours") + } - for i := 0; i < len(b); i++ { - c := b[i] + minutes, err := parseDecimalDigits(b[4:6]) + if err != nil { + return time.Time{}, err + } + if minutes > 59 { + return time.Time{}, unstable.NewParserError(b[4:6], "invalid timezone offset minutes") + } - switch c { - case '_': - if !before { - return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") - } - if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') { - return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore before exponent") - } - before = false - case '+', '-': - // signed exponents - cleaned = append(cleaned, c) - before = false - case 'e', 'E': - if i < len(b)-1 && b[i+1] == '_' { - return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after exponent") - } - cleaned = append(cleaned, c) - case '.': - if i < len(b)-1 && b[i+1] == '_' { - return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after decimal point") - } - if i > 0 && b[i-1] == '_' { - return nil, unstable.NewParserError(b[i-1:i], "cannot have underscore before decimal point") - } - cleaned = append(cleaned, c) - default: - before = true - cleaned = append(cleaned, c) + seconds := direction * (hours*3600 + minutes*60) + if seconds == 0 { + zone = time.UTC + } else { + zone = time.FixedZone("", seconds) } + b = b[dateTimeByteLen:] } - return cleaned, nil -} - -// isValidDate checks if a provided date is a date that exists. -func isValidDate(year int, month int, day int) bool { - return month > 0 && month < 13 && day > 0 && day <= daysIn(month, year) -} - -// daysBefore[m] counts the number of days in a non-leap year -// before month m begins. There is an entry for m=12, counting -// the number of days before January of next year (365). -var daysBefore = [...]int32{ - 0, - 31, - 31 + 28, - 31 + 28 + 31, - 31 + 28 + 31 + 30, - 31 + 28 + 31 + 30 + 31, - 31 + 28 + 31 + 30 + 31 + 30, - 31 + 28 + 31 + 30 + 31 + 30 + 31, - 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, - 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, - 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, - 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, - 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, -} - -func daysIn(m int, year int) int { - if m == 2 && isLeap(year) { - return 29 + if len(b) > 0 { + return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone") } - return int(daysBefore[m] - daysBefore[m-1]) -} -func isLeap(year int) bool { - return year%4 == 0 && (year%100 != 0 || year%400 == 0) -} + t := time.Date( + dt.Year, + time.Month(dt.Month), + dt.Day, + dt.Hour, + dt.Minute, + dt.Second, + dt.Nanosecond, + zone) -func isDigit(r byte) bool { - return r >= '0' && r <= '9' + return t, nil } diff --git a/vendor/github.com/pelletier/go-toml/v2/decode_fused.go b/vendor/github.com/pelletier/go-toml/v2/decode_fused.go new file mode 100644 index 00000000..9de2d652 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/decode_fused.go @@ -0,0 +1,362 @@ +package toml + +import ( + "errors" + "reflect" + "strings" + + "github.com/pelletier/go-toml/v2/internal/parserbridge" + "github.com/pelletier/go-toml/v2/unstable" +) + +// unmarshalFused decodes a whole document into a native map[string]interface{} +// tree with no reflection on the document structure, and without building an +// AST for table headers and scalar key-values. Only container values (arrays +// and inline tables) are parsed into the parser arena, so that the seen-tracker +// can validate them and decodeAny can presize the resulting slices and maps — +// the AST is what makes that cheap O(1) presizing possible. +// +// It is used when the target is a fully generic value (interface{} or +// map[string]interface{}) and the unmarshaler interface is disabled. The +// seen-tracker validates the document (duplicate keys, type consistency), so +// the builder creates and merges containers without revalidating. Strict mode +// never applies to a generic target (a map has no "unknown fields"), and +// captures never apply (a generic value implements no Unmarshaler). +func (d *decoder) unmarshalFused(root reflect.Value, data []byte) error { + var m map[string]interface{} + if !root.IsNil() { + // Decode into (merge with) an existing generic map when present. + if em, ok := root.Interface().(map[string]interface{}); ok { + m = em + } + } + if m == nil { + m = map[string]interface{}{} + } + + if err := d.fusedDocument(m, data); err != nil { + return d.wrapFusedError(data, err) + } + + if root.CanSet() { + root.Set(reflect.ValueOf(m)) + } + return nil +} + +// fusedDocument runs the top-level expression loop, mirroring +// Parser.NextExpression but storing values directly into native maps. +func (d *decoder) fusedDocument(m map[string]interface{}, b []byte) error { + cur := m + for { + b = fusedSkipWS(b) + if len(b) == 0 { + return nil + } + switch b[0] { + case '\n': + b = b[1:] + case '\r': + if len(b) > 1 && b[1] == '\n' { + b = b[2:] + continue + } + return unstable.NewParserError(b[:1], "expected newline but got %#U", b[0]) + case '#': + _, rest, err := parserbridge.ScanComment(b) + if err != nil { + return err + } + rest, err = fusedConsumeEOL(rest) + if err != nil { + return err + } + b = rest + case '[': + rest, err := d.fusedTable(b, m, &cur) + if err != nil { + return err + } + b = rest + default: + rest, err := d.fusedKeyVal(b, cur) + if err != nil { + return err + } + b = rest + } + } +} + +// fusedTable handles a [table] or [[array table]] header. b starts at '['. It +// updates *cur to the table the following key-values belong to. +func (d *decoder) fusedTable(b []byte, root map[string]interface{}, cur *map[string]interface{}) ([]byte, error) { + arrayTable := len(b) > 1 && b[1] == '[' + + var start []byte + if arrayTable { + start = fusedSkipWS(b[2:]) + } else { + start = fusedSkipWS(b[1:]) + } + + var err error + var rawKey []byte + d.keyParts, rawKey, b, err = parserbridge.ScanKey(&d.p, start, d.keyParts[:0]) + if err != nil { + return nil, err + } + + if arrayTable { + if len(b) < 2 || b[0] != ']' || b[1] != ']' { + return nil, unstable.NewParserError(fusedHL1(b), "expected ']]' to close array table name") + } + b = b[2:] + } else { + if len(b) == 0 || b[0] != ']' { + return nil, unstable.NewParserError(fusedHL1(b), "expected ']' to close table name") + } + b = b[1:] + } + + // The whole expression (including its line termination) is parsed before + // it is validated, to keep error precedence identical to the AST path. + b, err = d.fusedFinishLine(b) + if err != nil { + return nil, err + } + + if arrayTable { + first, err := d.seen.CheckArrayTable(d.keyParts) + if err != nil { + return nil, d.fusedSeenError(rawKey, d.keyParts, err) + } + *cur = d.anyArrayTableParts(root, d.keyParts, first) + } else { + if _, err := d.seen.CheckTable(d.keyParts); err != nil { + return nil, d.fusedSeenError(rawKey, d.keyParts, err) + } + *cur = d.anyTableParts(root, d.keyParts) + } + return b, nil +} + +// fusedKeyVal handles a `key = value` expression relative to the current table +// cur. b starts at the first character of the key. +func (d *decoder) fusedKeyVal(b []byte, cur map[string]interface{}) ([]byte, error) { + var err error + var rawKey []byte + d.keyParts, rawKey, b, err = parserbridge.ScanKey(&d.p, b, d.keyParts[:0]) + if err != nil { + return nil, err + } + if len(b) == 0 || b[0] != '=' { + return nil, unstable.NewParserError(fusedHL1(b), "expected '=' after key") + } + b = fusedSkipWS(b[1:]) + if len(b) == 0 { + return nil, unstable.NewParserError(b, "expected value, not end of input") + } + + if c := b[0]; c == '[' || c == '{' { + // Container value: build its AST so the seen-tracker can validate it + // and decodeAny can presize the resulting slices and maps. + nodeAny, rest, err := parserbridge.ParseValue(&d.p, b) + if err != nil { + return nil, err + } + node := nodeAny.(*unstable.Node) + rest, err = d.fusedFinishLine(rest) + if err != nil { + return nil, err + } + leafID, err := d.seen.CheckKeyValue(d.keyParts) + if err != nil { + return nil, d.fusedSeenError(rawKey, d.keyParts, err) + } + if err := d.seen.CheckValueUnder(leafID, node); err != nil { + return nil, d.fusedSeenError(rawKey, d.keyParts, err) + } + av, err := d.decodeAny(node) + if err != nil { + return nil, err + } + d.setFusedLeaf(cur, d.keyParts, av) + return rest, nil + } + + // Scalar value: scan it without building a node, then validate and convert + // it natively. + k, _, value, rest, err := parserbridge.ScanScalar(&d.p, b) + if err != nil { + return nil, err + } + kind := unstable.Kind(k) + rest, err = d.fusedFinishLine(rest) + if err != nil { + return nil, err + } + if _, err := d.seen.CheckKeyValue(d.keyParts); err != nil { + return nil, d.fusedSeenError(rawKey, d.keyParts, err) + } + av, err := d.fusedScalar(kind, value) + if err != nil { + return nil, err + } + d.setFusedLeaf(cur, d.keyParts, av) + return rest, nil +} + +// fusedSeenError turns a bare error returned by a SeenTracker parts-method +// into a ParserError carrying the position (the raw key span) and key path of +// the offending expression, so that it is reported as a DecodeError with +// context. It mirrors decoder.wrapSeenError for the fused (AST-less) path. +func (d *decoder) fusedSeenError(rawKey []byte, parts [][]byte, err error) error { + key := make(Key, len(parts)) + for i, p := range parts { + key[i] = string(p) + } + return &unstable.ParserError{ + Highlight: rawKey, + Message: strings.TrimPrefix(err.Error(), "toml: "), + Key: key, + } +} + +// fusedScalar converts a scanned scalar value into the native Go value used +// for generic targets. It mirrors the scalar cases of decodeAny. +func (d *decoder) fusedScalar(kind unstable.Kind, value []byte) (interface{}, error) { + switch kind { + case unstable.String: + return string(value), nil + case unstable.Integer: + i, err := parseInteger(value) + return i, err + case unstable.Float: + f, err := parseFloat(value) + return f, err + case unstable.Bool: + return value[0] == 't', nil + case unstable.DateTime: + t, err := parseDateTime(value) + return t, err + case unstable.LocalDateTime: + dt, rest, err := parseLocalDateTime(value) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, unstable.NewParserError(rest, "extra characters at the end of a local date time") + } + return dt, nil + case unstable.LocalDate: + date, err := parseLocalDate(value) + return date, err + case unstable.LocalTime: + t, rest, err := parseLocalTime(value) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, unstable.NewParserError(rest, "extra characters at the end of a local time") + } + return t, nil + default: + return nil, unstable.NewParserError(value, "unsupported value kind %s", kind) + } +} + +// anyTableParts navigates a [table] header (given its key parts) to the map it +// designates, creating intermediate tables as needed. +func (d *decoder) anyTableParts(m map[string]interface{}, parts [][]byte) map[string]interface{} { + cur := m + for _, p := range parts { + cur = d.anyChildTable(cur, d.intern(p)) + } + return cur +} + +// anyArrayTableParts navigates a [[array table]] header (given its key parts), +// appends a fresh element to the designated array, and returns it. first is +// true the first time this header is seen, in which case any pre-existing array +// (from a reused target) is reset. +func (d *decoder) anyArrayTableParts(m map[string]interface{}, parts [][]byte, first bool) map[string]interface{} { + cur := m + name := d.intern(parts[0]) + for i := 1; i < len(parts); i++ { + cur = d.anyChildTable(cur, name) + name = d.intern(parts[i]) + } + s, _ := cur[name].([]interface{}) + if first { + s = s[:0] + } + elem := map[string]interface{}{} + cur[name] = append(s, elem) + return elem +} + +// setFusedLeaf assigns av at the (possibly dotted) key parts within cur, +// creating intermediate maps as needed. +func (d *decoder) setFusedLeaf(cur map[string]interface{}, parts [][]byte, av interface{}) { + for i := 0; i < len(parts)-1; i++ { + cur = d.anyChildTable(cur, d.intern(parts[i])) + } + cur[d.intern(parts[len(parts)-1])] = av +} + +// wrapFusedError gives document context to errors produced by the fused +// decoder. +func (d *decoder) wrapFusedError(data []byte, err error) error { + var perr *unstable.ParserError + if errors.As(err, &perr) && len(perr.Highlight) == 0 { + // Mirror NextExpression: give end-of-input errors a usable position by + // extending the empty highlight to the last byte of the document. + if offset := cap(data) - cap(perr.Highlight); offset > 0 && offset == len(data) { + perr.Highlight = data[offset-1 : offset] + } + } + return d.wrapError(data, err) +} + +func fusedSkipWS(b []byte) []byte { + for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') { + b = b[1:] + } + return b +} + +func fusedConsumeEOL(b []byte) ([]byte, error) { + if len(b) == 0 { + return b, nil + } + switch b[0] { + case '\n': + return b[1:], nil + case '\r': + if len(b) > 1 && b[1] == '\n' { + return b[2:], nil + } + } + return nil, unstable.NewParserError(b[:1], "expected newline but got %#U", b[0]) +} + +// fusedFinishLine consumes `ws [comment] (newline|eof)` after an expression. +func (d *decoder) fusedFinishLine(b []byte) ([]byte, error) { + b = fusedSkipWS(b) + if len(b) > 0 && b[0] == '#' { + _, rest, err := parserbridge.ScanComment(b) + if err != nil { + return nil, err + } + b = rest + } + return fusedConsumeEOL(b) +} + +func fusedHL1(b []byte) []byte { + if len(b) > 0 { + return b[:1] + } + return b +} diff --git a/vendor/github.com/pelletier/go-toml/v2/errors.go b/vendor/github.com/pelletier/go-toml/v2/errors.go index 309733f1..8e10b128 100644 --- a/vendor/github.com/pelletier/go-toml/v2/errors.go +++ b/vendor/github.com/pelletier/go-toml/v2/errors.go @@ -1,11 +1,10 @@ package toml import ( - "fmt" + "errors" "strconv" "strings" - "github.com/pelletier/go-toml/v2/internal/danger" "github.com/pelletier/go-toml/v2/unstable" ) @@ -47,13 +46,24 @@ func (s *StrictMissingError) String() string { if i > 0 { buf.WriteString("\n---\n") } - buf.WriteString(e.String()) } return buf.String() } +// Unwrap returns wrapped decode errors +// +// Implements errors.Join() interface. +func (s *StrictMissingError) Unwrap() []error { + errs := make([]error, len(s.Errors)) + for i := range s.Errors { + errs[i] = &s.Errors[i] + } + return errs +} + +// Key represents a TOML key as a sequence of key parts. type Key []string // Error returns the error message contained in the DecodeError. @@ -61,7 +71,8 @@ func (e *DecodeError) Error() string { return "toml: " + e.message } -// String returns the human-readable contextualized error. This string is multi-line. +// String returns the human-readable contextualized error. This string is +// multi-line. func (e *DecodeError) String() string { return e.human } @@ -72,181 +83,151 @@ func (e *DecodeError) Position() (row int, column int) { return e.line, e.column } -// Key that was being processed when the error occurred. The key is present only -// if this DecodeError is part of a StrictMissingError. +// Key that was being processed when the error occurred. func (e *DecodeError) Key() Key { return e.key } -// decodeErrorFromHighlight creates a DecodeError referencing a highlighted -// range of bytes from document. -// -// highlight needs to be a sub-slice of document, or this function panics. -// -// The function copies all bytes used in DecodeError, so that document and -// highlight can be freely deallocated. -// -//nolint:funlen +// wrapDecodeError creates a DecodeError from a ParserError. The highlight of +// the ParserError needs to be a subslice of the document. func wrapDecodeError(document []byte, de *unstable.ParserError) *DecodeError { - offset := danger.SubsliceOffset(document, de.Highlight) - - errMessage := de.Error() - errLine, errColumn := positionAtEnd(document[:offset]) - before, after := linesOfContext(document, de.Highlight, offset, 3) - - var buf strings.Builder + if de == nil { + return nil + } + return newDecodeError(document, de.Highlight, de.Key, de.Message) +} - maxLine := errLine + len(after) - 1 - lineColumnWidth := len(strconv.Itoa(maxLine)) +// newDecodeError creates a DecodeError pointing at the given highlight, which +// needs to be a subslice of the document. +func newDecodeError(document []byte, highlight []byte, key Key, message string) *DecodeError { + offset := subsliceOffset(document, highlight) - // Write the lines of context strictly before the error. - for i := len(before) - 1; i > 0; i-- { - line := errLine - i - buf.WriteString(formatLineNumber(line, lineColumnWidth)) - buf.WriteString("|") + errLineIdx, errColumn := positionAt(document, offset) - if len(before[i]) > 0 { - buf.WriteString(" ") - buf.Write(before[i]) - } + human := buildHumanContext(document, errLineIdx, errColumn, len(highlight), message) - buf.WriteRune('\n') + return &DecodeError{ + message: message, + line: errLineIdx + 1, + column: errColumn, + key: key, + human: human, } +} - // Write the document line that contains the error. - - buf.WriteString(formatLineNumber(errLine, lineColumnWidth)) - buf.WriteString("| ") - - if len(before) > 0 { - buf.Write(before[0]) +// subsliceOffset returns the offset of the subslice b within the document. +func subsliceOffset(document, b []byte) int { + // Highlights are subslices of the document, which means they share the + // same backing array, and their capacity counts the bytes between their + // start and the end of the backing array. + offset := cap(document) - cap(b) + if offset < 0 || offset+len(b) > len(document) { + panic(errors.New("highlight is not a subslice of the document")) } + return offset +} - buf.Write(de.Highlight) - - if len(after) > 0 { - buf.Write(after[0]) +// positionAt returns the 0-indexed line and the 1-indexed column of the given +// offset in the document. +func positionAt(document []byte, offset int) (lineIdx int, column int) { + lineStart := 0 + for i := 0; i < offset; i++ { + if document[i] == '\n' { + lineIdx++ + lineStart = i + 1 + } } + return lineIdx, offset - lineStart + 1 +} - buf.WriteRune('\n') - - // Write the line with the error message itself (so it does not have a line - // number). - - buf.WriteString(strings.Repeat(" ", lineColumnWidth)) - buf.WriteString("| ") - - if len(before) > 0 { - buf.WriteString(strings.Repeat(" ", len(before[0]))) +// docLines splits the document into lines, removing the trailing newline +// characters. +func docLines(document []byte) []string { + s := string(document) + lines := strings.Split(s, "\n") + for i, l := range lines { + lines[i] = strings.TrimSuffix(l, "\r") } + return lines +} - buf.WriteString(strings.Repeat("~", len(de.Highlight))) +// buildHumanContext renders the human-readable multi-line context of an +// error: a window of up to 3 lines before and after the error line, with +// the error position underlined. +func buildHumanContext(document []byte, errLineIdx, errColumn, highlightLen int, message string) string { + lines := docLines(document) - if len(errMessage) > 0 { - buf.WriteString(" ") - buf.WriteString(errMessage) + const window = 3 + firstIdx := errLineIdx - window + if firstIdx < 0 { + firstIdx = 0 } - - // Write the lines of context strictly after the error. - - for i := 1; i < len(after); i++ { - buf.WriteRune('\n') - line := errLine + i - buf.WriteString(formatLineNumber(line, lineColumnWidth)) - buf.WriteString("|") - - if len(after[i]) > 0 { - buf.WriteString(" ") - buf.Write(after[i]) - } + lastIdx := errLineIdx + window + if lastIdx > len(lines)-1 { + lastIdx = len(lines) - 1 } - - return &DecodeError{ - message: errMessage, - line: errLine, - column: errColumn, - key: de.Key, - human: buf.String(), + // Empty lines at the edges of the window are dropped, unless the error + // is about that very position. + for firstIdx < errLineIdx && lines[firstIdx] == "" { + firstIdx++ + } + for lastIdx > errLineIdx && lines[lastIdx] == "" { + lastIdx-- } -} -func formatLineNumber(line int, width int) string { - format := "%" + strconv.Itoa(width) + "d" + // Width of the column of line numbers. + width := len(strconv.Itoa(lastIdx + 1)) - return fmt.Sprintf(format, line) -} - -func linesOfContext(document []byte, highlight []byte, offset int, linesAround int) ([][]byte, [][]byte) { - return beforeLines(document, offset, linesAround), afterLines(document, highlight, offset, linesAround) -} + var buf strings.Builder -func beforeLines(document []byte, offset int, linesAround int) [][]byte { - var beforeLines [][]byte - - // Walk the document backward from the highlight to find previous lines - // of context. - rest := document[:offset] -backward: - for o := len(rest) - 1; o >= 0 && len(beforeLines) <= linesAround && len(rest) > 0; { - switch { - case rest[o] == '\n': - // handle individual lines - beforeLines = append(beforeLines, rest[o+1:]) - rest = rest[:o] - o = len(rest) - 1 - case o == 0: - // add the first line only if it's non-empty - beforeLines = append(beforeLines, rest) - - break backward - default: - o-- + writeLine := func(idx int) { + number := strconv.Itoa(idx + 1) + for i := len(number); i < width; i++ { + buf.WriteByte(' ') } + buf.WriteString(number) + buf.WriteByte('|') + if len(lines[idx]) > 0 { + buf.WriteByte(' ') + buf.WriteString(lines[idx]) + } + buf.WriteByte('\n') } - return beforeLines -} + for idx := firstIdx; idx <= errLineIdx; idx++ { + writeLine(idx) + } -func afterLines(document []byte, highlight []byte, offset int, linesAround int) [][]byte { - var afterLines [][]byte - - // Walk the document forward from the highlight to find the following - // lines of context. - rest := document[offset+len(highlight):] -forward: - for o := 0; o < len(rest) && len(afterLines) <= linesAround; { - switch { - case rest[o] == '\n': - // handle individual lines - afterLines = append(afterLines, rest[:o]) - rest = rest[o+1:] - o = 0 - - case o == len(rest)-1: - // add last line only if it's non-empty - afterLines = append(afterLines, rest) - - break forward - default: - o++ + // Underline the error. + for i := 0; i < width; i++ { + buf.WriteByte(' ') + } + buf.WriteString("| ") + for i := 1; i < errColumn; i++ { + buf.WriteByte(' ') + } + // The highlight cannot extend past the end of its line. + tildes := highlightLen + if errLineIdx < len(lines) { + if avail := len(lines[errLineIdx]) - errColumn + 1; tildes > avail { + tildes = avail } } + if tildes < 1 { + tildes = 1 + } + for i := 0; i < tildes; i++ { + buf.WriteByte('~') + } + if message != "" { + buf.WriteByte(' ') + buf.WriteString(message) + } + buf.WriteByte('\n') - return afterLines -} - -func positionAtEnd(b []byte) (row int, column int) { - row = 1 - column = 1 - - for _, c := range b { - if c == '\n' { - row++ - column = 1 - } else { - column++ - } + for idx := errLineIdx + 1; idx <= lastIdx; idx++ { + writeLine(idx) } - return + return strings.TrimSuffix(buf.String(), "\n") } diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go b/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go deleted file mode 100644 index 80f698db..00000000 --- a/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go +++ /dev/null @@ -1,42 +0,0 @@ -package characters - -var invalidAsciiTable = [256]bool{ - 0x00: true, - 0x01: true, - 0x02: true, - 0x03: true, - 0x04: true, - 0x05: true, - 0x06: true, - 0x07: true, - 0x08: true, - // 0x09 TAB - // 0x0A LF - 0x0B: true, - 0x0C: true, - // 0x0D CR - 0x0E: true, - 0x0F: true, - 0x10: true, - 0x11: true, - 0x12: true, - 0x13: true, - 0x14: true, - 0x15: true, - 0x16: true, - 0x17: true, - 0x18: true, - 0x19: true, - 0x1A: true, - 0x1B: true, - 0x1C: true, - 0x1D: true, - 0x1E: true, - 0x1F: true, - // 0x20 - 0x7E Printable ASCII characters - 0x7F: true, -} - -func InvalidAscii(b byte) bool { - return invalidAsciiTable[b] -} diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go b/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go deleted file mode 100644 index db4f45ac..00000000 --- a/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go +++ /dev/null @@ -1,199 +0,0 @@ -package characters - -import ( - "unicode/utf8" -) - -type utf8Err struct { - Index int - Size int -} - -func (u utf8Err) Zero() bool { - return u.Size == 0 -} - -// Verified that a given string is only made of valid UTF-8 characters allowed -// by the TOML spec: -// -// Any Unicode character may be used except those that must be escaped: -// quotation mark, backslash, and the control characters other than tab (U+0000 -// to U+0008, U+000A to U+001F, U+007F). -// -// It is a copy of the Go 1.17 utf8.Valid implementation, tweaked to exit early -// when a character is not allowed. -// -// The returned utf8Err is Zero() if the string is valid, or contains the byte -// index and size of the invalid character. -// -// quotation mark => already checked -// backslash => already checked -// 0-0x8 => invalid -// 0x9 => tab, ok -// 0xA - 0x1F => invalid -// 0x7F => invalid -func Utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { - // Fast path. Check for and skip 8 bytes of ASCII characters per iteration. - offset := 0 - for len(p) >= 8 { - // Combining two 32 bit loads allows the same code to be used - // for 32 and 64 bit platforms. - // The compiler can generate a 32bit load for first32 and second32 - // on many platforms. See test/codegen/memcombine.go. - first32 := uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 - second32 := uint32(p[4]) | uint32(p[5])<<8 | uint32(p[6])<<16 | uint32(p[7])<<24 - if (first32|second32)&0x80808080 != 0 { - // Found a non ASCII byte (>= RuneSelf). - break - } - - for i, b := range p[:8] { - if InvalidAscii(b) { - err.Index = offset + i - err.Size = 1 - return - } - } - - p = p[8:] - offset += 8 - } - n := len(p) - for i := 0; i < n; { - pi := p[i] - if pi < utf8.RuneSelf { - if InvalidAscii(pi) { - err.Index = offset + i - err.Size = 1 - return - } - i++ - continue - } - x := first[pi] - if x == xx { - // Illegal starter byte. - err.Index = offset + i - err.Size = 1 - return - } - size := int(x & 7) - if i+size > n { - // Short or invalid. - err.Index = offset + i - err.Size = n - i - return - } - accept := acceptRanges[x>>4] - if c := p[i+1]; c < accept.lo || accept.hi < c { - err.Index = offset + i - err.Size = 2 - return - } else if size == 2 { - } else if c := p[i+2]; c < locb || hicb < c { - err.Index = offset + i - err.Size = 3 - return - } else if size == 3 { - } else if c := p[i+3]; c < locb || hicb < c { - err.Index = offset + i - err.Size = 4 - return - } - i += size - } - return -} - -// Return the size of the next rune if valid, 0 otherwise. -func Utf8ValidNext(p []byte) int { - c := p[0] - - if c < utf8.RuneSelf { - if InvalidAscii(c) { - return 0 - } - return 1 - } - - x := first[c] - if x == xx { - // Illegal starter byte. - return 0 - } - size := int(x & 7) - if size > len(p) { - // Short or invalid. - return 0 - } - accept := acceptRanges[x>>4] - if c := p[1]; c < accept.lo || accept.hi < c { - return 0 - } else if size == 2 { - } else if c := p[2]; c < locb || hicb < c { - return 0 - } else if size == 3 { - } else if c := p[3]; c < locb || hicb < c { - return 0 - } - - return size -} - -// acceptRange gives the range of valid values for the second byte in a UTF-8 -// sequence. -type acceptRange struct { - lo uint8 // lowest value for second byte. - hi uint8 // highest value for second byte. -} - -// acceptRanges has size 16 to avoid bounds checks in the code that uses it. -var acceptRanges = [16]acceptRange{ - 0: {locb, hicb}, - 1: {0xA0, hicb}, - 2: {locb, 0x9F}, - 3: {0x90, hicb}, - 4: {locb, 0x8F}, -} - -// first is information about the first byte in a UTF-8 sequence. -var first = [256]uint8{ - // 1 2 3 4 5 6 7 8 9 A B C D E F - as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F - as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F - as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F - as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F - as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F - as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F - as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F - as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F - // 1 2 3 4 5 6 7 8 9 A B C D E F - xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F - xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F - xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF - xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF - xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF - s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF - s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF - s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF -} - -const ( - // The default lowest and highest continuation byte. - locb = 0b10000000 - hicb = 0b10111111 - - // These names of these constants are chosen to give nice alignment in the - // table below. The first nibble is an index into acceptRanges or F for - // special one-byte cases. The second nibble is the Rune length or the - // Status for the special one-byte case. - xx = 0xF1 // invalid: size 1 - as = 0xF0 // ASCII: size 1 - s1 = 0x02 // accept 0, size 2 - s2 = 0x13 // accept 1, size 3 - s3 = 0x03 // accept 0, size 3 - s4 = 0x23 // accept 2, size 3 - s5 = 0x34 // accept 3, size 4 - s6 = 0x04 // accept 0, size 4 - s7 = 0x44 // accept 4, size 4 -) diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go b/vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go deleted file mode 100644 index e38e1131..00000000 --- a/vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go +++ /dev/null @@ -1,65 +0,0 @@ -package danger - -import ( - "fmt" - "reflect" - "unsafe" -) - -const maxInt = uintptr(int(^uint(0) >> 1)) - -func SubsliceOffset(data []byte, subslice []byte) int { - datap := (*reflect.SliceHeader)(unsafe.Pointer(&data)) - hlp := (*reflect.SliceHeader)(unsafe.Pointer(&subslice)) - - if hlp.Data < datap.Data { - panic(fmt.Errorf("subslice address (%d) is before data address (%d)", hlp.Data, datap.Data)) - } - offset := hlp.Data - datap.Data - - if offset > maxInt { - panic(fmt.Errorf("slice offset larger than int (%d)", offset)) - } - - intoffset := int(offset) - - if intoffset > datap.Len { - panic(fmt.Errorf("slice offset (%d) is farther than data length (%d)", intoffset, datap.Len)) - } - - if intoffset+hlp.Len > datap.Len { - panic(fmt.Errorf("slice ends (%d+%d) is farther than data length (%d)", intoffset, hlp.Len, datap.Len)) - } - - return intoffset -} - -func BytesRange(start []byte, end []byte) []byte { - if start == nil || end == nil { - panic("cannot call BytesRange with nil") - } - startp := (*reflect.SliceHeader)(unsafe.Pointer(&start)) - endp := (*reflect.SliceHeader)(unsafe.Pointer(&end)) - - if startp.Data > endp.Data { - panic(fmt.Errorf("start pointer address (%d) is after end pointer address (%d)", startp.Data, endp.Data)) - } - - l := startp.Len - endLen := int(endp.Data-startp.Data) + endp.Len - if endLen > l { - l = endLen - } - - if l > startp.Cap { - panic(fmt.Errorf("range length is larger than capacity")) - } - - return start[:l] -} - -func Stride(ptr unsafe.Pointer, size uintptr, offset int) unsafe.Pointer { - // TODO: replace with unsafe.Add when Go 1.17 is released - // https://github.com/golang/go/issues/40481 - return unsafe.Pointer(uintptr(ptr) + uintptr(int(size)*offset)) -} diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go b/vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go deleted file mode 100644 index 9d41c28a..00000000 --- a/vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go +++ /dev/null @@ -1,23 +0,0 @@ -package danger - -import ( - "reflect" - "unsafe" -) - -// typeID is used as key in encoder and decoder caches to enable using -// the optimize runtime.mapaccess2_fast64 function instead of the more -// expensive lookup if we were to use reflect.Type as map key. -// -// typeID holds the pointer to the reflect.Type value, which is unique -// in the program. -// -// https://github.com/segmentio/encoding/blob/master/json/codec.go#L59-L61 -type TypeID unsafe.Pointer - -func MakeTypeID(t reflect.Type) TypeID { - // reflect.Type has the fields: - // typ unsafe.Pointer - // ptr unsafe.Pointer - return TypeID((*[2]unsafe.Pointer)(unsafe.Pointer(&t))[1]) -} diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/parserbridge/parserbridge.go b/vendor/github.com/pelletier/go-toml/v2/internal/parserbridge/parserbridge.go new file mode 100644 index 00000000..703aceea --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/internal/parserbridge/parserbridge.go @@ -0,0 +1,37 @@ +// Package parserbridge exposes the unstable parser's non-AST scanners to the +// root toml package without making them part of the unstable public API. +// +// The fused generic-decode fast path needs to scan keys, scalars and comments +// (and parse container values into the arena) without going through the +// AST-pushing NextExpression/Expression methods. Those scanners depend on +// Parser internals (the string-unescape scratch buffer and the node arena), so +// they have to live in the unstable package; but they are an implementation +// detail of the decoder, not something we want to commit to in the public API. +// +// The unstable package populates these variables in its init; the toml package +// reads them. The parser is passed as an any (it is always an *unstable.Parser) +// and the scalar kind is an int (it is always an unstable.Kind) so that this +// package imports neither unstable nor toml, avoiding an import cycle. Passing +// a pointer through an interface does not allocate, so the fused path keeps its +// allocation profile. +package parserbridge + +var ( + // ScanScalar scans a single scalar value (string, integer, float, bool or + // date/time) without building an AST node. kind is an unstable.Kind. + ScanScalar func(p any, b []byte) (kind int, raw, value, rest []byte, err error) + + // ScanKey scans a (possibly dotted) key without building AST nodes, + // appending each decoded part to dst. + ScanKey func(p any, b []byte, dst [][]byte) (parts [][]byte, raw, rest []byte, err error) + + // ScanComment scans a comment starting at '#', returning the comment bytes + // (including '#', excluding the line ending) and the rest of the input. It + // needs no parser state. + ScanComment func(b []byte) (comment, rest []byte, err error) + + // ParseValue parses a single value (including arrays and inline tables) into + // the parser arena, returning the root *unstable.Node and the rest of the + // input. + ParseValue func(p any, b []byte) (node any, rest []byte, err error) +) diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go index 149b17f5..661b11cf 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go +++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go @@ -14,7 +14,8 @@ func (t *KeyTracker) UpdateTable(node *unstable.Node) { t.Push(node) } -// UpdateArrayTable sets the state of the tracker with the AST array table node. +// UpdateArrayTable sets the state of the tracker with the AST array table +// node. func (t *KeyTracker) UpdateArrayTable(node *unstable.Node) { t.reset() t.Push(node) @@ -36,7 +37,7 @@ func (t *KeyTracker) Pop(node *unstable.Node) { } } -// Key returns the current key +// Key returns the current key. func (t *KeyTracker) Key() []string { k := make([]string, len(t.k)) copy(k, t.k) diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go index 76df2d5b..e88cd941 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go +++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go @@ -3,7 +3,6 @@ package tracker import ( "bytes" "fmt" - "sync" "github.com/pelletier/go-toml/v2/unstable" ) @@ -12,9 +11,21 @@ type keyKind uint8 const ( invalidKind keyKind = iota + // valueKind is a regular value (scalar, array, or inline table). It + // cannot be extended. valueKind + // kvTableKind is a table created implicitly by a dotted key. It can only + // be extended by other dotted keys. + kvTableKind + // tableKind is a table created by a [header]. The explicit flag tells + // whether the table was created by its own header (true) or as an + // intermediate step of a longer key (false). tableKind + // arrayTableKind is an array of tables created by [[header]]. arrayTableKind + // anonymousKind is an entry that cannot be looked up by name. It serves + // as the parent of the content of inline tables stored inside arrays. + anonymousKind ) func (k keyKind) String() string { @@ -23,22 +34,36 @@ func (k keyKind) String() string { return "invalid" case valueKind: return "value" + case kvTableKind: + return "kv-table" case tableKind: return "table" case arrayTableKind: - return "array table" + return "array-table" + case anonymousKind: + return "anonymous" } panic("missing keyKind string mapping") } +// entry represents a node that has been seen in the document. Its size has a +// direct impact on the performance of unmarshaling documents: keep it as +// small as possible. +type entry struct { + parent int32 + kind keyKind + explicit bool + name []byte +} + // SeenTracker tracks which keys have been seen with which TOML type to flag // duplicates and mismatches according to the spec. // -// Each node in the visited tree is represented by an entry. Each entry has an -// identifier, which is provided by a counter. Entries are stored in the array -// entries. As new nodes are discovered (referenced for the first time in the -// TOML document), entries are created and appended to the array. An entry -// points to its parent using its id. +// Each node in the visited tree is represented by an entry. Each entry has +// an identifier, which is provided by a counter. Entries are stored in the +// array entries. As new nodes are discovered (referenced for the first time +// in the TOML document), entries are created and appended to the array. An +// entry points to its parent using its id. // // To find whether a given key (sequence of []byte) has already been visited, // the entries are linearly searched, looking for one with the right name and @@ -53,306 +78,373 @@ func (k keyKind) String() string { // invariant above, the deletion process needs to keep the order of entries. // This results in more copies in that case. type SeenTracker struct { - entries []entry - currentIdx int -} + entries []entry + currentTable int32 -var pool = sync.Pool{ - New: func() interface{} { - return &SeenTracker{} - }, + // scratch buffers for clear() + removedBuf []bool + remapBuf []int32 } -func (s *SeenTracker) reset() { - // Always contains a root element at index 0. - s.currentIdx = 0 - if len(s.entries) == 0 { - s.entries = make([]entry, 1, 2) - } else { - s.entries = s.entries[:1] - } - s.entries[0].child = -1 - s.entries[0].next = -1 +// Reset brings the tracker to its initial state, with just a root table, so +// that it can be reused across documents. +func (s *SeenTracker) Reset() { + s.reset() } -type entry struct { - // Use -1 to indicate no child or no sibling. - child int - next int - - name []byte - kind keyKind - explicit bool - kv bool +// reset brings the tracker to its initial state, with just a root table. +func (s *SeenTracker) reset() { + s.entries = append(s.entries[:0], entry{ + parent: -1, + kind: tableKind, + }) + s.currentTable = 0 } -// Find the index of the child of parentIdx with key k. Returns -1 if -// it does not exist. -func (s *SeenTracker) find(parentIdx int, k []byte) int { - for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next { - if bytes.Equal(s.entries[i].name, k) { - return i +// find returns the id of the entry with the given parent and name, or -1. +// Anonymous entries are never returned. +func (s *SeenTracker) find(parent int32, name []byte) int32 { + // Children always appear after their parent. + for i := int(parent) + 1; i < len(s.entries); i++ { + e := &s.entries[i] + if e.parent == parent && e.kind != anonymousKind && bytes.Equal(e.name, name) { + return int32(i) //nolint:gosec // entry counts are bounded by document size } } return -1 } -// Remove all descendants of node at position idx. -func (s *SeenTracker) clear(idx int) { - if idx >= len(s.entries) { - return - } - - for i := s.entries[idx].child; i >= 0; { - next := s.entries[i].next - n := s.entries[0].next - s.entries[0].next = i - s.entries[i].next = n - s.entries[i].name = nil - s.clear(i) - i = next - } - - s.entries[idx].child = -1 -} - -func (s *SeenTracker) create(parentIdx int, name []byte, kind keyKind, explicit bool, kv bool) int { - e := entry{ - child: -1, - next: s.entries[parentIdx].child, - - name: name, +// create appends a new entry and returns its id. +func (s *SeenTracker) create(parent int32, name []byte, kind keyKind, explicit bool) int32 { + id := int32(len(s.entries)) //nolint:gosec // entry counts are bounded by document size + s.entries = append(s.entries, entry{ + parent: parent, kind: kind, explicit: explicit, - kv: kv, + name: name, + }) + return id +} + +// clear removes all the descendants of the entry with the given id, keeping +// the order of the remaining entries. +func (s *SeenTracker) clear(id int32) { + // Compute which entries are removed. Given that children always appear + // after their parent, a single forward pass is enough. + if cap(s.removedBuf) < len(s.entries) { + s.removedBuf = make([]bool, len(s.entries)) + s.remapBuf = make([]int32, len(s.entries)) } - var idx int - if s.entries[0].next >= 0 { - idx = s.entries[0].next - s.entries[0].next = s.entries[idx].next - s.entries[idx] = e - } else { - idx = len(s.entries) - s.entries = append(s.entries, e) + removed := s.removedBuf[:len(s.entries)] + remap := s.remapBuf[:len(s.entries)] + for i := range removed { + removed[i] = false } - s.entries[parentIdx].child = idx - - return idx -} - -func (s *SeenTracker) setExplicitFlag(parentIdx int) { - for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next { - if s.entries[i].kv { - s.entries[i].explicit = true - s.entries[i].kv = false + n := int32(0) + for i := 0; i < len(s.entries); i++ { + parent := s.entries[i].parent + if parent >= 0 && (parent == id && s.entries[i].kind != invalidKind || removed[parent]) { + removed[i] = true + continue } - s.setExplicitFlag(i) + remap[i] = n + if int32(i) != n { //nolint:gosec // entry counts are bounded by document size + e := s.entries[i] + e.parent = remap[e.parent] + s.entries[n] = e + } + n++ } + s.entries = s.entries[:n] } // CheckExpression takes a top-level node and checks that it does not contain // keys that have been seen in previous calls, and validates that types are -// consistent. It returns true if it is the first time this node's key is seen. -// Useful to clear array tables on first use. +// consistent. It returns true if it is the first time this node's key is +// seen. Useful to clear array tables on first use. func (s *SeenTracker) CheckExpression(node *unstable.Node) (bool, error) { - if s.entries == nil { + if len(s.entries) == 0 { s.reset() } switch node.Kind { case unstable.KeyValue: - return s.checkKeyValue(node) + return false, s.checkKeyValue(s.currentTable, node) case unstable.Table: return s.checkTable(node) case unstable.ArrayTable: return s.checkArrayTable(node) default: - panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind)) + return false, fmt.Errorf("toml: unexpected expression kind %s", node.Kind) } } -func (s *SeenTracker) checkTable(node *unstable.Node) (bool, error) { - if s.currentIdx >= 0 { - s.setExplicitFlag(s.currentIdx) - } - - it := node.Key() - - parentIdx := 0 - - // This code is duplicated in checkArrayTable. This is because factoring - // it in a function requires to copy the iterator, or allocate it to the - // heap, which is not cheap. - for it.Next() { - if it.IsLast() { - break +// CheckTable validates a [table] header given the decoded parts of its key. +// It mirrors checkTable but is driven directly from the key parts instead of +// an AST, for callers that decode without building one. It returns whether the +// table is seen for the first time. +func (s *SeenTracker) CheckTable(parts [][]byte) (bool, error) { + parent := int32(0) + for k := 0; k < len(parts); k++ { + name := parts[k] + if k == len(parts)-1 { + // Final part of the key. + i := s.find(parent, name) + if i < 0 { + i = s.create(parent, name, tableKind, true) + s.currentTable = i + return true, nil + } + e := &s.entries[i] + switch e.kind { + case tableKind: + if e.explicit { + return false, fmt.Errorf("toml: table %s already exists", name) + } + e.explicit = true + s.currentTable = i + return false, nil + case kvTableKind: + return false, fmt.Errorf("toml: table %s already exists as defined by a dotted key", name) + case arrayTableKind: + return false, fmt.Errorf("toml: table %s already exists as an array of tables", name) + default: + return false, fmt.Errorf("toml: key %s should be a table, not a %s", name, e.kind) + } } - k := it.Node().Data + i := s.find(parent, name) + if i < 0 { + i = s.create(parent, name, tableKind, false) + } else { + switch s.entries[i].kind { + case tableKind, arrayTableKind, kvTableKind: + // Tables created by dotted keys can receive new sub-tables, + // but cannot be redefined (handled by the last-part case). + default: + return false, fmt.Errorf("toml: key %s already exists as a value", name) + } + } + parent = i + } + panic("unreachable: table expression without key") +} - idx := s.find(parentIdx, k) +// CheckArrayTable validates a [[array table]] header given the decoded parts +// of its key. It mirrors checkArrayTable but is driven directly from the key +// parts. It returns whether the array table is seen for the first time. +func (s *SeenTracker) CheckArrayTable(parts [][]byte) (bool, error) { + parent := int32(0) + for k := 0; k < len(parts); k++ { + name := parts[k] + if k == len(parts)-1 { + i := s.find(parent, name) + if i < 0 { + i = s.create(parent, name, arrayTableKind, true) + s.currentTable = i + return true, nil + } + if s.entries[i].kind != arrayTableKind { + return false, fmt.Errorf("toml: key %s already exists as a %s, but should be an array table", name, s.entries[i].kind) + } + // Make the descendants of this array table re-discoverable for + // the new element. + s.clear(i) + s.currentTable = i + return false, nil + } - if idx < 0 { - idx = s.create(parentIdx, k, tableKind, false, false) + i := s.find(parent, name) + if i < 0 { + i = s.create(parent, name, tableKind, false) } else { - entry := s.entries[idx] - if entry.kind == valueKind { - return false, fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) + switch s.entries[i].kind { + case tableKind, arrayTableKind, kvTableKind: + // Tables created by dotted keys can receive new sub-tables, + // but cannot be redefined (handled by the last-part case). + default: + return false, fmt.Errorf("toml: key %s already exists as a value", name) } } - parentIdx = idx + parent = i } + panic("unreachable: array table expression without key") +} - k := it.Node().Data - idx := s.find(parentIdx, k) - - first := false - if idx >= 0 { - kind := s.entries[idx].kind - if kind != tableKind { - return false, fmt.Errorf("toml: key %s should be a table, not a %s", string(k), kind) +// CheckKeyValue validates the (possibly dotted) key of a key-value under the +// current table, WITHOUT validating its value. It returns the id of the leaf +// entry, so the caller can validate a container value with CheckValueUnder. +func (s *SeenTracker) CheckKeyValue(parts [][]byte) (int32, error) { + parent := s.currentTable + for k := 0; k < len(parts); k++ { + name := parts[k] + if k == len(parts)-1 { + if i := s.find(parent, name); i >= 0 { + return -1, fmt.Errorf("toml: key %s is already defined", name) + } + return s.create(parent, name, valueKind, false), nil } - if s.entries[idx].explicit { - return false, fmt.Errorf("toml: table %s already exists", string(k)) + + i := s.find(parent, name) + if i < 0 { + i = s.create(parent, name, kvTableKind, false) + } else if s.entries[i].kind != kvTableKind { + return -1, fmt.Errorf("toml: key %s is already defined", name) } - s.entries[idx].explicit = true - } else { - idx = s.create(parentIdx, k, tableKind, true, false) - first = true + parent = i } + panic("unreachable: key-value expression without key") +} - s.currentIdx = idx - - return first, nil +// CheckValueUnder validates the content of a value stored under the given +// entry (typically the leaf returned by CheckKeyValue): inline tables cannot +// contain duplicate keys, including in the inline tables and arrays they +// contain. +func (s *SeenTracker) CheckValueUnder(parent int32, value *unstable.Node) error { + return s.checkValue(parent, value) } -func (s *SeenTracker) checkArrayTable(node *unstable.Node) (bool, error) { - if s.currentIdx >= 0 { - s.setExplicitFlag(s.currentIdx) - } +func (s *SeenTracker) checkTable(node *unstable.Node) (bool, error) { + parent := int32(0) it := node.Key() - - parentIdx := 0 - + // Handle the intermediate parts of the key. for it.Next() { + part := it.Node() + name := part.Data if it.IsLast() { - break + // Final part of the key. + i := s.find(parent, name) + if i < 0 { + i = s.create(parent, name, tableKind, true) + s.currentTable = i + return true, nil + } + e := &s.entries[i] + switch e.kind { + case tableKind: + if e.explicit { + return false, fmt.Errorf("toml: table %s already exists", name) + } + e.explicit = true + s.currentTable = i + return false, nil + case kvTableKind: + return false, fmt.Errorf("toml: table %s already exists as defined by a dotted key", name) + case arrayTableKind: + return false, fmt.Errorf("toml: table %s already exists as an array of tables", name) + default: + return false, fmt.Errorf("toml: key %s should be a table, not a %s", name, e.kind) + } } - k := it.Node().Data - - idx := s.find(parentIdx, k) - - if idx < 0 { - idx = s.create(parentIdx, k, tableKind, false, false) + i := s.find(parent, name) + if i < 0 { + i = s.create(parent, name, tableKind, false) } else { - entry := s.entries[idx] - if entry.kind == valueKind { - return false, fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) + switch s.entries[i].kind { + case tableKind, arrayTableKind, kvTableKind: + // Tables created by dotted keys can receive new sub-tables, + // but cannot be redefined (handled by the last-part case). + default: + return false, fmt.Errorf("toml: key %s already exists as a value", name) } } - - parentIdx = idx + parent = i } - - k := it.Node().Data - idx := s.find(parentIdx, k) - - firstTime := idx < 0 - if firstTime { - idx = s.create(parentIdx, k, arrayTableKind, true, false) - } else { - kind := s.entries[idx].kind - if kind != arrayTableKind { - return false, fmt.Errorf("toml: key %s already exists as a %s, but should be an array table", kind, string(k)) - } - s.clear(idx) - } - - s.currentIdx = idx - - return firstTime, nil + panic("unreachable: table expression without key") } -func (s *SeenTracker) checkKeyValue(node *unstable.Node) (bool, error) { - parentIdx := s.currentIdx - it := node.Key() +func (s *SeenTracker) checkArrayTable(node *unstable.Node) (bool, error) { + parent := int32(0) + it := node.Key() for it.Next() { - k := it.Node().Data - - idx := s.find(parentIdx, k) + part := it.Node() + name := part.Data + if it.IsLast() { + i := s.find(parent, name) + if i < 0 { + i = s.create(parent, name, arrayTableKind, true) + s.currentTable = i + return true, nil + } + if s.entries[i].kind != arrayTableKind { + return false, fmt.Errorf("toml: key %s already exists as a %s, but should be an array table", name, s.entries[i].kind) + } + // Make the descendants of this array table re-discoverable for + // the new element. + s.clear(i) + // Note: clear cannot move i because i comes before all its + // descendants. + s.currentTable = i + return false, nil + } - if idx < 0 { - idx = s.create(parentIdx, k, tableKind, false, true) + i := s.find(parent, name) + if i < 0 { + i = s.create(parent, name, tableKind, false) } else { - entry := s.entries[idx] - if it.IsLast() { - return false, fmt.Errorf("toml: key %s is already defined", string(k)) - } else if entry.kind != tableKind { - return false, fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) - } else if entry.explicit { - return false, fmt.Errorf("toml: cannot redefine table %s that has already been explicitly defined", string(k)) + switch s.entries[i].kind { + case tableKind, arrayTableKind, kvTableKind: + // Tables created by dotted keys can receive new sub-tables, + // but cannot be redefined (handled by the last-part case). + default: + return false, fmt.Errorf("toml: key %s already exists as a value", name) } } - - parentIdx = idx + parent = i } - - s.entries[parentIdx].kind = valueKind - - value := node.Value() - - switch value.Kind { - case unstable.InlineTable: - return s.checkInlineTable(value) - case unstable.Array: - return s.checkArray(value) - } - - return false, nil + panic("unreachable: array table expression without key") } -func (s *SeenTracker) checkArray(node *unstable.Node) (first bool, err error) { - it := node.Children() +func (s *SeenTracker) checkKeyValue(parent int32, node *unstable.Node) error { + it := node.Key() for it.Next() { - n := it.Node() - switch n.Kind { - case unstable.InlineTable: - first, err = s.checkInlineTable(n) - if err != nil { - return false, err - } - case unstable.Array: - first, err = s.checkArray(n) - if err != nil { - return false, err + part := it.Node() + name := part.Data + if it.IsLast() { + if i := s.find(parent, name); i >= 0 { + return fmt.Errorf("toml: key %s is already defined", name) } + id := s.create(parent, name, valueKind, false) + return s.checkValue(id, node.Value()) } + + i := s.find(parent, name) + if i < 0 { + i = s.create(parent, name, kvTableKind, false) + } else if s.entries[i].kind != kvTableKind { + return fmt.Errorf("toml: key %s is already defined", name) + } + parent = i } - return first, nil + panic("unreachable: key-value expression without key") } -func (s *SeenTracker) checkInlineTable(node *unstable.Node) (first bool, err error) { - s = pool.Get().(*SeenTracker) - s.reset() - - it := node.Children() - for it.Next() { - n := it.Node() - first, err = s.checkKeyValue(n) - if err != nil { - return false, err +// checkValue verifies the content of a value: inline tables cannot contain +// duplicate keys, including in the inline tables and arrays they contain. +func (s *SeenTracker) checkValue(id int32, value *unstable.Node) error { + switch value.Kind { + case unstable.InlineTable: + it := value.Children() + for it.Next() { + if err := s.checkKeyValue(id, it.Node()); err != nil { + return err + } + } + case unstable.Array: + it := value.Children() + for it.Next() { + elem := it.Node() + if elem.Kind == unstable.InlineTable || elem.Kind == unstable.Array { + elemID := s.create(id, nil, anonymousKind, false) + if err := s.checkValue(elemID, elem); err != nil { + return err + } + } } + default: } - - // As inline tables are self-contained, the tracker does not - // need to retain the details of what they contain. The - // keyValue element that creates the inline table is kept to - // mark the presence of the inline table and prevent - // redefinition of its keys: check* functions cannot walk into - // a value. - pool.Put(s) - return first, nil + return nil } diff --git a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go index bf031739..ed510382 100644 --- a/vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go +++ b/vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go @@ -1 +1,2 @@ +// Package tracker provides functions for keeping track of AST nodes. package tracker diff --git a/vendor/github.com/pelletier/go-toml/v2/localtime.go b/vendor/github.com/pelletier/go-toml/v2/localtime.go index a856bfdb..eb8c20e6 100644 --- a/vendor/github.com/pelletier/go-toml/v2/localtime.go +++ b/vendor/github.com/pelletier/go-toml/v2/localtime.go @@ -45,7 +45,7 @@ func (d *LocalDate) UnmarshalText(b []byte) error { type LocalTime struct { Hour int // Hour of the day: [0; 24[ Minute int // Minute of the hour: [0; 60[ - Second int // Second of the minute: [0; 60[ + Second int // Second of the minute: [0; 59] Nanosecond int // Nanoseconds within the second: [0, 1000000000[ Precision int // Number of digits to display for Nanosecond. } @@ -62,7 +62,7 @@ func (d LocalTime) String() string { } else if d.Nanosecond > 0 { // Nanoseconds are specified, but precision is not provided. Use the // minimum. - s += strings.Trim(fmt.Sprintf(".%09d", d.Nanosecond), "0") + s += strings.TrimRight(fmt.Sprintf(".%09d", d.Nanosecond), "0") } return s @@ -77,7 +77,7 @@ func (d LocalTime) MarshalText() ([]byte, error) { func (d *LocalTime) UnmarshalText(b []byte) error { res, left, err := parseLocalTime(b) if err == nil && len(left) != 0 { - err = unstable.NewParserError(left, "extra characters") + err = unstable.NewParserError(left, "extra characters at the end of a local time") } if err != nil { return err @@ -111,12 +111,11 @@ func (d LocalDateTime) MarshalText() ([]byte, error) { func (d *LocalDateTime) UnmarshalText(data []byte) error { res, left, err := parseLocalDateTime(data) if err == nil && len(left) != 0 { - err = unstable.NewParserError(left, "extra characters") + err = unstable.NewParserError(left, "extra characters at the end of a local date time") } if err != nil { return err } - *d = res return nil } diff --git a/vendor/github.com/pelletier/go-toml/v2/marshaler.go b/vendor/github.com/pelletier/go-toml/v2/marshaler.go index 161acd93..7f6a9408 100644 --- a/vendor/github.com/pelletier/go-toml/v2/marshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/marshaler.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding" "encoding/json" + "errors" "fmt" "io" "math" @@ -11,25 +12,37 @@ import ( "slices" "strconv" "strings" + "sync" "time" - "unicode" + "unicode/utf8" - "github.com/pelletier/go-toml/v2/internal/characters" + "github.com/pelletier/go-toml/v2/internal/parserbridge" + "github.com/pelletier/go-toml/v2/unstable" ) // Marshal serializes a Go value as a TOML document. // // It is a shortcut for Encoder.Encode() with the default options. func Marshal(v interface{}) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) + enc := Encoder{indentSymbol: " "} - err := enc.Encode(v) + e := encoderStatePool.Get().(*encoderState) + e.Encoder = &enc + e.marshalerOn = enc.marshalerInterface + e.buf = e.buf[:0] + e.keyStack = e.keyStack[:0] + e.lastWasHeader = false + + err := e.encodeRoot(v) if err != nil { + encoderStatePool.Put(e) return nil, err } - return buf.Bytes(), nil + out := make([]byte, len(e.buf)) + copy(out, e.buf) + encoderStatePool.Put(e) + return out, nil } // Encoder writes a TOML document to an output stream. @@ -42,7 +55,10 @@ type Encoder struct { arraysMultiline bool indentSymbol string indentTables bool - marshalJsonNumbers bool + marshalJSONNumbers bool + + // toggles the unstable.Marshaler interface + marshalerInterface bool } // NewEncoder returns a new Encoder that writes to w. @@ -55,8 +71,8 @@ func NewEncoder(w io.Writer) *Encoder { // SetTablesInline forces the encoder to emit all tables inline. // -// This behavior can be controlled on an individual struct field basis with the -// inline tag: +// This behavior can be controlled on an individual struct field basis with +// the inline tag: // // MyField `toml:",inline"` func (enc *Encoder) SetTablesInline(inline bool) *Encoder { @@ -64,10 +80,11 @@ func (enc *Encoder) SetTablesInline(inline bool) *Encoder { return enc } -// SetArraysMultiline forces the encoder to emit all arrays with one element per -// line. +// SetArraysMultiline forces the encoder to emit all arrays with one element +// per line. // -// This behavior can be controlled on an individual struct field basis with the multiline tag: +// This behavior can be controlled on an individual struct field basis with +// the multiline tag: // // MyField `multiline:"true"` func (enc *Encoder) SetArraysMultiline(multiline bool) *Encoder { @@ -89,14 +106,43 @@ func (enc *Encoder) SetIndentTables(indent bool) *Encoder { return enc } -// SetMarshalJsonNumbers forces the encoder to serialize `json.Number` as a +// SetMarshalJSONNumbers forces the encoder to serialize `json.Number` as a // float or integer instead of relying on TextMarshaler to emit a string. // // *Unstable:* This method does not follow the compatibility guarantees of // semver. It can be changed or removed without a new major version being // issued. -func (enc *Encoder) SetMarshalJsonNumbers(indent bool) *Encoder { - enc.marshalJsonNumbers = indent +func (enc *Encoder) SetMarshalJSONNumbers(indent bool) *Encoder { + enc.marshalJSONNumbers = indent + return enc +} + +// EnableMarshalerInterface enables the unstable.Marshaler interface. +// +// With this feature enabled, types implementing the unstable.Marshaler +// interface emit their own raw TOML instead of being encoded structurally. It +// is the encoding counterpart of Decoder.EnableUnmarshalerInterface, and allows +// types such as unstable.RawMessage to round-trip raw TOML bytes. +// +// The bytes returned by MarshalTOML are spliced into the document verbatim. The +// encoder parses them to decide between the valid positions: +// - bytes forming a single value are emitted inline, as in `key = `; +// - bytes forming key-value lines are emitted as the body of a `[key]` table; +// - at the document root, the bytes are emitted as the whole document: the +// encode counterpart of the decoder delivering the whole document to a +// root unstable.Unmarshaler. +// +// An empty result is omitted from the output. Bytes that are not valid TOML +// for their position result in an error, as do bytes forming table content in +// a position where only a value is valid (an array element, an inline table, +// or a table forced inline). MarshalTOML can be called more than once for the +// same value during a single encode, so it must be deterministic. +// +// *Unstable:* This method does not follow the compatibility guarantees of +// semver. It can be changed or removed without a new major version being +// issued. +func (enc *Encoder) EnableMarshalerInterface() *Encoder { + enc.marshalerInterface = true return enc } @@ -118,9 +164,9 @@ func (enc *Encoder) SetMarshalJsonNumbers(indent bool) *Encoder { // // Intermediate tables are always printed. // -// By default, strings are encoded as literal string, unless they contain either -// a newline character or a single quote. In that case they are emitted as -// quoted strings. +// By default, strings are encoded as literal string, unless they contain +// either a newline character or a single quote. In that case they are emitted +// as quoted strings. // // Unsigned integers larger than math.MaxInt64 cannot be encoded. Doing so // results in an error. This rule exists because the TOML specification only @@ -129,8 +175,8 @@ func (enc *Encoder) SetMarshalJsonNumbers(indent bool) *Encoder { // readable (at best) by other implementations. To encode such numbers, a // solution is a custom type that implements encoding.TextMarshaler. // -// When encoding structs, fields are encoded in order of definition, with their -// exact name. +// When encoding structs, fields are encoded in order of definition, with +// their exact name. // // Tables and array tables are separated by empty lines. However, consecutive // subtables definitions are not. For example: @@ -150,984 +196,1502 @@ func (enc *Encoder) SetMarshalJsonNumbers(indent bool) *Encoder { // The encoding of each public struct field can be customized by the format // string in the "toml" key of the struct field's tag. This follows // encoding/json's convention. The format string starts with the name of the -// field, optionally followed by a comma-separated list of options. The name may -// be empty in order to provide options without overriding the default name. +// field, optionally followed by a comma-separated list of options. The name +// may be empty in order to provide options without overriding the default +// name. // -// The "multiline" option emits strings as quoted multi-line TOML strings. It -// has no effect on fields that would not be encoded as strings. +// The "multiline" option emits strings as quoted multi-line TOML strings, and +// arrays with one element per line. For strings, it only takes effect when the +// value contains a newline; single-line values are emitted as regular strings. +// It has no effect on fields that would not be encoded as strings or arrays. // -// The "inline" option turns fields that would be emitted as tables into inline -// tables instead. It has no effect on other fields. +// The "inline" option turns fields that would be emitted as tables into +// inline tables instead. It has no effect on other fields. // // The "omitempty" option prevents empty values or groups from being emitted. // -// The "commented" option prefixes the value and all its children with a comment -// symbol. +// The "omitzero" option prevents zero values or groups from being emitted. // -// In addition to the "toml" tag struct tag, a "comment" tag can be used to emit -// a TOML comment before the value being annotated. Comments are ignored inside -// inline tables. For array tables, the comment is only present before the first -// element of the array. +// The "commented" option prefixes the value and all its children with a +// comment symbol. +// +// In addition to the "toml" tag struct tag, a "comment" tag can be used to +// emit a TOML comment before the value being annotated. Comments are ignored +// inside inline tables. For array tables, the comment is only present before +// the first element of the array. func (enc *Encoder) Encode(v interface{}) error { - var ( - b []byte - ctx encoderCtx - ) - - ctx.inline = enc.tablesInline - - if v == nil { - return fmt.Errorf("toml: cannot encode a nil interface") - } - - b, err := enc.encode(b, ctx, reflect.ValueOf(v)) + e := encoderStatePool.Get().(*encoderState) + e.Encoder = enc + e.marshalerOn = enc.marshalerInterface + e.buf = e.buf[:0] + e.keyStack = e.keyStack[:0] + e.lastWasHeader = false + + err := e.encodeRoot(v) if err != nil { + encoderStatePool.Put(e) return err } - _, err = enc.w.Write(b) + _, err = enc.w.Write(e.buf) + encoderStatePool.Put(e) if err != nil { return fmt.Errorf("toml: cannot write: %w", err) } - return nil } -type valueOptions struct { - multiline bool - omitempty bool - commented bool - comment string +var encoderStatePool = sync.Pool{ + New: func() interface{} { return &encoderState{} }, } -type encoderCtx struct { - // Current top-level key. - parentKey []string +type encoderState struct { + *Encoder - // Key that should be used for a KV. - key string - // Extra flag to account for the empty string - hasKey bool + buf []byte - // Set to true to indicate that the encoder is inside a KV, so that all - // tables need to be inlined. - insideKv bool + // keyStack is the dotted key of the table being encoded, shared by the + // whole encode as a stack. + keyStack []string - // Set to true to skip the first table header in an array table. - skipTableHeader bool + // entriesPool recycles entry slices across tables of the same encode. + entriesPool [][]entry - // Should the next table be encoded as inline - inline bool + // lastWasHeader is true when the last line written was a table header, + // used to avoid empty lines between consecutive table definitions. + lastWasHeader bool - // Indentation level - indent int + // stringKeyBuf is a reusable buffer to read string map keys without + // allocating one per map. + stringKeyBuf reflect.Value - // Prefix the current value with a comment. - commented bool + // parser classifies the bytes returned by unstable.Marshaler values as a + // single value or a table body. Only used when marshalerOn is set. + parser unstable.Parser - // Options coming from struct tags - options valueOptions + // marshalerOn mirrors Encoder.marshalerInterface, copied onto the state so + // the per-value hot path checks a direct field instead of dereferencing the + // embedded *Encoder on every value. + marshalerOn bool } -func (ctx *encoderCtx) shiftKey() { - if ctx.hasKey { - ctx.parentKey = append(ctx.parentKey, ctx.key) - ctx.clearKey() - } +// valueOptions are the encoding options attached to one entry of a table. +type valueOptions struct { + multiline bool + inline bool + omitempty bool + omitzero bool + commented bool + // rawShape is the unstable.Marshaler classification of the entry's value, + // resolved at encode time (not from tags). It lives here, in the byte of + // padding the booleans already leave, so that entry does not grow and the + // encoder's default (interface-disabled) path keeps its exact layout and + // performance. It stays shapeUnknown for everything that is not a Marshaler. + rawShape rawShape + comment string } -func (ctx *encoderCtx) setKey(k string) { - ctx.key = k - ctx.hasKey = true +// entry is a deferred key-value of a table being encoded. +type entry struct { + key string + value reflect.Value + options valueOptions } -func (ctx *encoderCtx) clearKey() { - ctx.key = "" - ctx.hasKey = false -} +// rawShape classifies the bytes produced by an unstable.Marshaler. +type rawShape uint8 + +const ( + // shapeUnknown is the default: not an unstable.Marshaler, or the interface + // is disabled. + shapeUnknown rawShape = iota + // shapeEmpty is whitespace-only content: it has no TOML representation and + // is omitted from the output. + shapeEmpty + // shapeValue is a single TOML value, emitted inline as `key = `. + shapeValue + // shapeTable is one or more key-value lines, emitted as a `[key]` body. + shapeTable +) -func (ctx *encoderCtx) isRoot() bool { - return len(ctx.parentKey) == 0 && !ctx.hasKey -} +func (e *encoderState) encodeRoot(v interface{}) error { + if v == nil { + return errors.New("toml: cannot encode a nil interface") + } -func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { - i := v.Interface() + rv := reflect.ValueOf(v) + rv, ok := resolve(rv) + if !ok { + return errors.New("toml: cannot encode a nil pointer") + } - switch x := i.(type) { - case time.Time: - if x.Nanosecond() > 0 { - return x.AppendFormat(b, time.RFC3339Nano), nil - } - return x.AppendFormat(b, time.RFC3339), nil - case LocalTime: - return append(b, x.String()...), nil - case LocalDate: - return append(b, x.String()...), nil - case LocalDateTime: - return append(b, x.String()...), nil - case json.Number: - if enc.marshalJsonNumbers { - if x == "" { /// Useful zero value. - return append(b, "0"...), nil - } else if v, err := x.Int64(); err == nil { - return enc.encode(b, ctx, reflect.ValueOf(v)) - } else if f, err := x.Float64(); err == nil { - return enc.encode(b, ctx, reflect.ValueOf(f)) - } else { - return nil, fmt.Errorf("toml: unable to convert %q to int64 or float64", x) - } - } + if e.marshalerOn && encPropsForType(rv.Type()).marshaler != 0 { + return e.encodeMarshalerRoot(rv) } - hasTextMarshaler := v.Type().Implements(textMarshalerType) - if hasTextMarshaler || (v.CanAddr() && reflect.PointerTo(v.Type()).Implements(textMarshalerType)) { - if !hasTextMarshaler { - v = v.Addr() + switch rv.Kind() { + case reflect.Map, reflect.Struct: + if isValueKind(rv) { + return fmt.Errorf("toml: cannot encode a %s as a document root", rv.Type()) } + return e.encodeTable(rv, false, 0) + default: + return fmt.Errorf("toml: cannot encode a %s as a document root", rv.Type()) + } +} - if ctx.isRoot() { - return nil, fmt.Errorf("toml: type %s implementing the TextMarshaler interface cannot be a root element", v.Type()) +// encodeMarshalerRoot emits the bytes of a root-level unstable.Marshaler as +// the whole document: the encode counterpart of the decoder delivering the +// whole document to a root Unmarshaler. The bytes must form a TOML document +// (key-value lines and table headers); empty output produces an empty +// document. +func (e *encoderState) encodeMarshalerRoot(rv reflect.Value) error { + raw, err := e.marshalerBytes(rv) + if err != nil { + return err + } + trimmed := bytes.TrimSpace(raw) + if len(trimmed) == 0 { + return nil + } + if err := e.validateRawTableBody(rv.Type(), trimmed); err != nil { + // A single TOML value has no meaning at the document root; report it + // as such rather than as a syntax error. + if shape, _ := e.classifyRaw(trimmed); shape == shapeValue { + return fmt.Errorf("toml: cannot encode %s as a document root: MarshalTOML returned a single TOML value, not a document", rv.Type()) } + return err + } + e.buf = append(e.buf, trimmed...) + e.buf = append(e.buf, '\n') + e.lastWasHeader = false + return nil +} - text, err := v.Interface().(encoding.TextMarshaler).MarshalText() - if err != nil { - return nil, err +// resolve unwraps pointers and interfaces until a concrete value is found. +// Returns false if it resolves to nil. +func resolve(v reflect.Value) (reflect.Value, bool) { + for { + switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { + return v, false + } + v = v.Elem() + case reflect.Interface: + if v.IsNil() { + return v, false + } + v = v.Elem() + default: + return v, true } + } +} - b = enc.encodeString(b, string(text), ctx.options) +// typeEncProps caches the per-type facts used on every value encode. +type typeEncProps struct { + // 0: not a TextMarshaler, 1: the type implements it, 2: its pointer does + text uint8 + // 0: not an unstable.Marshaler, 1: the type implements it, 2: its pointer + // does. Only consulted when Encoder.marshalerInterface is set. + marshaler uint8 + // encoded as a TOML value (as opposed to a table) + isValue bool +} - return b, nil - } +var marshalerType = reflect.TypeOf(new(unstable.Marshaler)).Elem() - switch v.Kind() { - // containers - case reflect.Map: - return enc.encodeMap(b, ctx, v) - case reflect.Struct: - return enc.encodeStruct(b, ctx, v) - case reflect.Slice, reflect.Array: - return enc.encodeSlice(b, ctx, v) - case reflect.Interface: - if v.IsNil() { - return nil, fmt.Errorf("toml: encoding a nil interface is not supported") - } +var typeEncPropsCache sync.Map // reflect.Type -> typeEncProps - return enc.encode(b, ctx, v.Elem()) - case reflect.Ptr: - if v.IsNil() { - return enc.encode(b, ctx, reflect.Zero(v.Type().Elem())) +func encPropsForType(t reflect.Type) typeEncProps { + if p, ok := typeEncPropsCache.Load(t); ok { + return p.(typeEncProps) + } + var p typeEncProps + switch { + case t.Implements(textMarshalerType): + p.text = 1 + case reflect.PtrTo(t).Implements(textMarshalerType): + p.text = 2 + } + switch { + case t.Implements(marshalerType): + p.marshaler = 1 + case reflect.PtrTo(t).Implements(marshalerType): + p.marshaler = 2 + } + switch t { + case timeType, localDateType, localTimeType, localDateTimeType: + p.isValue = true + default: + if p.text != 0 { + p.isValue = true + } else { + switch t.Kind() { + case reflect.Map, reflect.Struct: + p.isValue = false + default: + p.isValue = true + } } + } + typeEncPropsCache.Store(t, p) + return p +} - return enc.encode(b, ctx, v.Elem()) +// isValueKind returns true when the resolved value is encoded as a TOML +// value (as opposed to a table). +func isValueKind(v reflect.Value) bool { + return encPropsForType(v.Type()).isValue +} - // values - case reflect.String: - b = enc.encodeString(b, v.String(), ctx.options) - case reflect.Float32: - f := v.Float() - - if math.IsNaN(f) { - b = append(b, "nan"...) - } else if f > math.MaxFloat32 { - b = append(b, "inf"...) - } else if f < -math.MaxFloat32 { - b = append(b, "-inf"...) - } else if math.Trunc(f) == f { - b = strconv.AppendFloat(b, f, 'f', 1, 32) - } else { - b = strconv.AppendFloat(b, f, 'f', -1, 32) +// isTableLike returns true when the value should be encoded as a table (or +// an array of tables for slices). +func (e *encoderState) isTableLike(v reflect.Value) bool { + v, ok := resolve(v) + if !ok { + // Unresolvable values (interface-held nil pointers) are encoded as + // the zero value of their element type by the value path. + return false + } + return !isValueKind(v) +} + +// isArrayOfTables returns true when the value is a non-empty slice or array +// containing only table-like values. +func (e *encoderState) isArrayOfTables(v reflect.Value) bool { + v, ok := resolve(v) + if !ok { + return false + } + if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { + return false + } + if v.Len() == 0 { + return false + } + // The Marshaler-aware classification lives in a separate method, hoisted out + // of this loop, so the default path keeps its exact original shape. + if e.marshalerOn { + return e.isMarshalerArrayOfTables(v) + } + for i := 0; i < v.Len(); i++ { + elem, ok := resolve(v.Index(i)) + if !ok || isValueKind(elem) { + return false } - case reflect.Float64: - f := v.Float() - if math.IsNaN(f) { - b = append(b, "nan"...) - } else if f > math.MaxFloat64 { - b = append(b, "inf"...) - } else if f < -math.MaxFloat64 { - b = append(b, "-inf"...) - } else if math.Trunc(f) == f { - b = strconv.AppendFloat(b, f, 'f', 1, 64) - } else { - b = strconv.AppendFloat(b, f, 'f', -1, 64) + } + return true +} + +// isMarshalerArrayOfTables is the EnableMarshalerInterface variant of +// isArrayOfTables: a Marshaler element counts as a table only when its raw +// content is table shaped (key-value lines); one holding a single value makes +// the whole slice a plain array instead. +func (e *encoderState) isMarshalerArrayOfTables(v reflect.Value) bool { + for i := 0; i < v.Len(); i++ { + elem, ok := resolve(v.Index(i)) + if !ok { + return false } - case reflect.Bool: - if v.Bool() { - b = append(b, "true"...) - } else { - b = append(b, "false"...) + if encPropsForType(elem.Type()).marshaler != 0 { + raw, err := e.marshalerBytes(elem) + if err != nil { + return false + } + if shape, _ := e.classifyRaw(raw); shape != shapeTable { + return false + } + continue } - case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint: - x := v.Uint() - if x > uint64(math.MaxInt64) { - return nil, fmt.Errorf("toml: not encoding uint (%d) greater than max int64 (%d)", x, int64(math.MaxInt64)) + if isValueKind(elem) { + return false } - b = strconv.AppendUint(b, x, 10) - case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: - b = strconv.AppendInt(b, v.Int(), 10) - default: - return nil, fmt.Errorf("toml: cannot encode value of type %s", v.Kind()) } - - return b, nil + return true } -func isNil(v reflect.Value) bool { - switch v.Kind() { - case reflect.Ptr, reflect.Interface, reflect.Map: - return v.IsNil() +// marshalerBytes returns the raw TOML produced by v's unstable.Marshaler +// implementation. The caller guarantees v implements the interface +// (encPropsForType(v.Type()).marshaler != 0). +func (e *encoderState) marshalerBytes(v reflect.Value) ([]byte, error) { + t := v.Type() + var m unstable.Marshaler + switch { + case encPropsForType(t).marshaler == 1: + // The type itself implements Marshaler (e.g. a value receiver). + m = v.Interface().(unstable.Marshaler) + case v.CanAddr(): + // Only the pointer implements it, and v is addressable. + m = v.Addr().Interface().(unstable.Marshaler) default: - return false + // Only the pointer implements it, but v is not addressable: take the + // address of a copy. + tmp := reflect.New(t) + tmp.Elem().Set(v) + m = tmp.Interface().(unstable.Marshaler) } + b, err := m.MarshalTOML() + if err != nil { + return nil, fmt.Errorf("toml: error calling MarshalTOML for type %s: %w", t, err) + } + return b, nil } -func shouldOmitEmpty(options valueOptions, v reflect.Value) bool { - return options.omitempty && isEmptyValue(v) +// validateRawTableBody checks that trimmed — table-shaped Marshaler output +// about to be spliced verbatim — is syntactically valid TOML, so a Marshaler +// cannot silently corrupt the document. It reuses e.parser, like classifyRaw. +func (e *encoderState) validateRawTableBody(t reflect.Type, trimmed []byte) error { + e.parser.Reset(trimmed) + for e.parser.NextExpression() { + } + if err := e.parser.Error(); err != nil { + return fmt.Errorf("toml: error calling MarshalTOML for type %s: invalid TOML: %w", t, err) + } + return nil } -func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) { - var err error - - if !ctx.inline { - b = enc.encodeComment(ctx.indent, options.comment, b) - b = enc.commented(ctx.commented, b) - b = enc.indent(ctx.indent, b) +// classifyRaw decides whether b (the output of an unstable.Marshaler) is empty, +// a single TOML value, or a table body. It returns the trimmed bytes that +// should be spliced into the document. It reuses e.parser, so it is not safe +// for concurrent use (encoderState is not shared). +func (e *encoderState) classifyRaw(b []byte) (rawShape, []byte) { + trimmed := bytes.TrimSpace(b) + if len(trimmed) == 0 { + return shapeEmpty, trimmed } + e.parser.Reset(trimmed) + _, rest, err := parserbridge.ParseValue(&e.parser, trimmed) + if err == nil && len(bytes.TrimSpace(rest)) == 0 { + return shapeValue, trimmed + } + return shapeTable, trimmed +} - b = enc.encodeKey(b, ctx.key) - b = append(b, " = "...) - - // create a copy of the context because the value of a KV shouldn't - // modify the global context. - subctx := ctx - subctx.insideKv = true - subctx.shiftKey() - subctx.options = options +// resolveMarshalerEntries classifies every entry whose value implements +// unstable.Marshaler, recording the shape on the entry so the two table passes +// can route it without re-classifying. Any error from MarshalTOML is surfaced +// eagerly. The marshaled bytes themselves are produced again at emit time by +// marshalerValue; that keeps entry small on the encoder's hot path, and only +// runs when the (opt-in) interface is enabled. +func (e *encoderState) resolveMarshalerEntries(entries []entry) error { + for i := range entries { + ent := &entries[i] + v, ok := resolve(ent.value) + if !ok || encPropsForType(v.Type()).marshaler == 0 { + continue + } + raw, err := e.marshalerBytes(v) + if err != nil { + return err + } + ent.options.rawShape, _ = e.classifyRaw(raw) + } + return nil +} - b, err = enc.encode(b, subctx, v) +// marshalerValue returns the trimmed bytes to splice for an entry already known +// to be an unstable.Marshaler (ent.options.rawShape != shapeUnknown). +func (e *encoderState) marshalerValue(ent *entry) ([]byte, error) { + v, _ := resolve(ent.value) + raw, err := e.marshalerBytes(v) if err != nil { return nil, err } - - return b, nil + return bytes.TrimSpace(raw), nil } -func (enc *Encoder) commented(commented bool, b []byte) []byte { - if commented { - return append(b, "# "...) +// appendMarshalerInline emits an unstable.Marshaler value in a value position +// (array element or inline-table member), where only a single TOML value is +// valid. done is false when t is not a Marshaler, so appendValue falls through +// to its normal handling. It is split out of appendValue (type check included) +// to keep that hot path lean. +func (e *encoderState) appendMarshalerInline(b []byte, v reflect.Value, t reflect.Type) (out []byte, done bool, err error) { + if encPropsForType(t).marshaler == 0 { + return b, false, nil + } + raw, err := e.marshalerBytes(v) + if err != nil { + return nil, true, err + } + shape, trimmed := e.classifyRaw(raw) + switch shape { + case shapeValue: + return append(b, trimmed...), true, nil + case shapeEmpty: + return nil, true, fmt.Errorf("toml: cannot encode an empty %s as an inline value", t) + default: + return nil, true, fmt.Errorf("toml: cannot encode %s as an inline value: %q is not a single TOML value", t, trimmed) } - return b } -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Struct: - return isEmptyStruct(v) - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() +// encodeTable writes the content of a table at the given key path. +func (e *encoderState) encodeTable(v reflect.Value, commented bool, indent int) error { + entries, err := e.collectEntries(v) + if err != nil { + return err } - return false -} -func isEmptyStruct(v reflect.Value) bool { - // TODO: merge with walkStruct and cache. - typ := v.Type() - for i := 0; i < typ.NumField(); i++ { - fieldType := typ.Field(i) + // Marshaler routing is hoisted behind a single local flag. When the (opt-in) + // interface is off, mOn is false and both passes run the exact baseline + // code, so the default Marshal path keeps its performance. + mOn := e.marshalerOn + if mOn { + // Classify Marshaler entries once, up front, so the passes can route + // them by shape and surface MarshalTOML errors eagerly. + if err := e.resolveMarshalerEntries(entries); err != nil { + return err + } + } - // only consider exported fields - if fieldType.PkgPath != "" { + // First pass: emit all key-values; tables are handled by the second + // pass. + for i := range entries { + ent := &entries[i] + if mOn { + switch ent.options.rawShape { + case shapeUnknown: + // Not a Marshaler: handled by the baseline logic below. + case shapeEmpty: + // No TOML representation: omit the key. + continue + case shapeValue: + if err := e.encodeKeyValue(*ent, commented, indent); err != nil { + return err + } + continue + case shapeTable: + // A table body is emitted in the second pass, unless it is + // forced inline (SetTablesInline / inline tag), which has no + // valid inline form and is reported as an error by + // encodeKeyValue. + if e.tablesInline || ent.options.inline { + if err := e.encodeKeyValue(*ent, commented, indent); err != nil { + return err + } + } + continue + } + } + if e.entryIsTable(ent) { continue } + if err := e.encodeKeyValue(*ent, commented, indent); err != nil { + return err + } + } - tag := fieldType.Tag.Get("toml") + // Second pass: emit the sub-tables, extending the shared key stack. + for i := range entries { + ent := entries[i] + if mOn { + switch ent.options.rawShape { + case shapeUnknown: + // Not a Marshaler: handled by the baseline logic below. + case shapeValue, shapeEmpty: + // Not a table: already handled (or omitted) in the first pass. + continue + case shapeTable: + // Emit the raw body verbatim under the freshly pushed header. + // (The forced-inline case already errored in the first pass.) + entCommented := commented || ent.options.commented + e.keyStack = append(e.keyStack, ent.key) + if err := e.encodeMarshalerTable(&ent, entCommented, indent); err != nil { + return err + } + e.keyStack = e.keyStack[:len(e.keyStack)-1] + continue + } + } + if !e.entryIsTable(&ent) { + continue + } + entCommented := commented || ent.options.commented + e.keyStack = append(e.keyStack, ent.key) - // special field name to skip field - if tag == "-" { + if e.isArrayOfTables(ent.value) { + err := e.encodeArrayTable(ent, entCommented, indent) + if err != nil { + return err + } + e.keyStack = e.keyStack[:len(e.keyStack)-1] continue } - f := v.Field(i) + // The value is resolvable: entryIsTable already resolved it. + tv, _ := resolve(ent.value) - if !isEmptyValue(f) { - return false + e.writeTableHeader(ent.options.comment, entCommented, false, indent) + + err := e.encodeTable(tv, entCommented, indent+1) + if err != nil { + return err } + e.keyStack = e.keyStack[:len(e.keyStack)-1] } - return true + e.putEntries(entries) + return nil } -const literalQuote = '\'' - -func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byte { - if needsQuoting(v) { - return enc.encodeQuotedString(options.multiline, b, v) +// encodeMarshalerTable emits a table-shaped unstable.Marshaler entry: the +// header for the key currently on the stack, then the raw body verbatim. +func (e *encoderState) encodeMarshalerTable(ent *entry, commented bool, indent int) error { + raw, err := e.marshalerValue(ent) + if err != nil { + return err } - - return enc.encodeLiteralString(b, v) + // Validate the bytes actually being spliced: MarshalTOML is called again + // for the emit, so this both rejects invalid TOML and guards against an + // implementation that returned different content than during + // classification. + if err := e.validateRawTableBody(ent.value.Type(), raw); err != nil { + return err + } + e.writeTableHeader(ent.options.comment, commented, false, indent) + e.spliceRawTableBody(raw, commented) + return nil } -func needsQuoting(v string) bool { - // TODO: vectorize - for _, b := range []byte(v) { - if b == '\'' || b == '\r' || b == '\n' || characters.InvalidAscii(b) { - return true - } +// spliceRawTableBody appends a raw table body verbatim after its header. When +// the table is commented, every physical line is prefixed with the comment +// marker so the body does not leak into the document as live keys. +func (e *encoderState) spliceRawTableBody(raw []byte, commented bool) { + if len(raw) == 0 { + return + } + if commented { + e.buf = append(e.buf, "# "...) + e.buf = append(e.buf, bytes.ReplaceAll(raw, []byte("\n"), []byte("\n# "))...) + } else { + e.buf = append(e.buf, raw...) } - return false + e.buf = append(e.buf, '\n') + e.lastWasHeader = false } -// caller should have checked that the string does not contain new lines or ' . -func (enc *Encoder) encodeLiteralString(b []byte, v string) []byte { - b = append(b, literalQuote) - b = append(b, v...) - b = append(b, literalQuote) - - return b +// entryIsTable reports whether the entry is emitted as a (sub-)table rather +// than a key-value. Marshaler entries are routed by encodeTable before this is +// reached, so it carries no marshaler-specific cost. +func (e *encoderState) entryIsTable(ent *entry) bool { + return !e.tablesInline && !ent.options.inline && (e.isTableLike(ent.value) || e.isArrayOfTables(ent.value)) } -func (enc *Encoder) encodeQuotedString(multiline bool, b []byte, v string) []byte { - stringQuote := `"` - - if multiline { - stringQuote = `"""` +// getEntries returns a reusable entry slice. +func (e *encoderState) getEntries() []entry { + if n := len(e.entriesPool); n > 0 { + s := e.entriesPool[n-1] + e.entriesPool = e.entriesPool[:n-1] + return s[:0] } + return nil +} - b = append(b, stringQuote...) - if multiline { - b = append(b, '\n') +// putEntries returns an entry slice to the pool. +func (e *encoderState) putEntries(s []entry) { + if cap(s) > 0 { + e.entriesPool = append(e.entriesPool, s) } +} - const ( - hextable = "0123456789ABCDEF" - // U+0000 to U+0008, U+000A to U+001F, U+007F - nul = 0x0 - bs = 0x8 - lf = 0xa - us = 0x1f - del = 0x7f - ) - - for _, r := range []byte(v) { - switch r { - case '\\': - b = append(b, `\\`...) - case '"': - b = append(b, `\"`...) - case '\b': - b = append(b, `\b`...) - case '\f': - b = append(b, `\f`...) - case '\n': - if multiline { - b = append(b, r) - } else { - b = append(b, `\n`...) +// encodeArrayTable writes all the elements of an array of tables. +func (e *encoderState) encodeArrayTable(ent entry, commented bool, indent int) error { + v, _ := resolve(ent.value) + comment := ent.options.comment + for i := 0; i < v.Len(); i++ { + // Elements are resolvable: isArrayOfTables already resolved them. + elem, _ := resolve(v.Index(i)) + + e.writeTableHeader(comment, commented, true, indent) + // The comment is only present before the first element. + comment = "" + + // A Marshaler element splices its raw table body verbatim. The shape + // was checked by isArrayOfTables, but MarshalTOML is called again for + // the emit, so the spliced bytes are validated here. + if e.marshalerOn && encPropsForType(elem.Type()).marshaler != 0 { + raw, err := e.marshalerBytes(elem) + if err != nil { + return err } - case '\r': - b = append(b, `\r`...) - case '\t': - b = append(b, `\t`...) - default: - switch { - case r >= nul && r <= bs, r >= lf && r <= us, r == del: - b = append(b, `\u00`...) - b = append(b, hextable[r>>4]) - b = append(b, hextable[r&0x0f]) - default: - b = append(b, r) + trimmed := bytes.TrimSpace(raw) + if err := e.validateRawTableBody(elem.Type(), trimmed); err != nil { + return err } + e.spliceRawTableBody(trimmed, commented) + continue } - } - - b = append(b, stringQuote...) - - return b -} -// caller should have checked that the string is in A-Z / a-z / 0-9 / - / _ . -func (enc *Encoder) encodeUnquotedKey(b []byte, v string) []byte { - return append(b, v...) + err := e.encodeTable(elem, commented, indent+1) + if err != nil { + return err + } + } + return nil } -func (enc *Encoder) encodeTableHeader(ctx encoderCtx, b []byte) ([]byte, error) { - if len(ctx.parentKey) == 0 { - return b, nil +// writeTableHeader emits a [table] or [[array table]] header line, preceded +// by an empty line and comments as needed. +func (e *encoderState) writeTableHeader(comment string, commented bool, array bool, indent int) { + key := e.keyStack + if len(e.buf) > 0 && !e.lastWasHeader { + e.buf = append(e.buf, '\n') } - b = enc.encodeComment(ctx.indent, ctx.options.comment, b) - - b = enc.commented(ctx.commented, b) - - b = enc.indent(ctx.indent, b) - - b = append(b, '[') + headerIndent := indent - b = enc.encodeKey(b, ctx.parentKey[0]) + e.writeComment(comment, headerIndent) - for _, k := range ctx.parentKey[1:] { - b = append(b, '.') - b = enc.encodeKey(b, k) + // The "commented" marker is emitted at column zero, ahead of any table + // indentation, so that the indentation appears inside the comment + // (e.g. `# [a.b]`). This matches the historical v2.3 layout. + if commented { + e.buf = append(e.buf, "# "...) } - - b = append(b, "]\n"...) - - return b, nil -} - -//nolint:cyclop -func (enc *Encoder) encodeKey(b []byte, k string) []byte { - needsQuotation := false - cannotUseLiteral := false - - if len(k) == 0 { - return append(b, "''"...) + e.writeIndent(headerIndent) + e.buf = append(e.buf, '[') + if array { + e.buf = append(e.buf, '[') } - - for _, c := range k { - if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' { - continue - } - - if c == literalQuote { - cannotUseLiteral = true + for i, part := range key { + if i > 0 { + e.buf = append(e.buf, '.') } - - needsQuotation = true + e.buf = e.appendKey(e.buf, part) } - - if needsQuotation && needsQuoting(k) { - cannotUseLiteral = true + e.buf = append(e.buf, ']') + if array { + e.buf = append(e.buf, ']') } + e.buf = append(e.buf, '\n') + e.lastWasHeader = true +} - switch { - case cannotUseLiteral: - return enc.encodeQuotedString(false, b, k) - case needsQuotation: - return enc.encodeLiteralString(b, k) - default: - return enc.encodeUnquotedKey(b, k) +func (e *encoderState) writeIndent(indent int) { + if !e.indentTables { + return + } + for i := 0; i < indent; i++ { + e.buf = append(e.buf, e.indentSymbol...) } } -func (enc *Encoder) keyToString(k reflect.Value) (string, error) { - keyType := k.Type() - switch { - case keyType.Kind() == reflect.String: - return k.String(), nil +// writeComment emits the comment lines attached to an entry. +func (e *encoderState) writeComment(comment string, indent int) { + if comment == "" { + return + } + for _, line := range strings.Split(comment, "\n") { + e.writeIndent(indent) + e.buf = append(e.buf, "# "...) + e.buf = append(e.buf, line...) + e.buf = append(e.buf, '\n') + } +} - case keyType.Implements(textMarshalerType): - keyB, err := k.Interface().(encoding.TextMarshaler).MarshalText() - if err != nil { - return "", fmt.Errorf("toml: error marshalling key %v from text: %w", k, err) - } - return string(keyB), nil +// encodeKeyValue writes one `key = value` line of a table. +func (e *encoderState) encodeKeyValue(ent entry, commented bool, indent int) error { + commented = commented || ent.options.commented - case keyType.Kind() == reflect.Int || keyType.Kind() == reflect.Int8 || keyType.Kind() == reflect.Int16 || keyType.Kind() == reflect.Int32 || keyType.Kind() == reflect.Int64: - return strconv.FormatInt(k.Int(), 10), nil + e.writeComment(ent.options.comment, indent) - case keyType.Kind() == reflect.Uint || keyType.Kind() == reflect.Uint8 || keyType.Kind() == reflect.Uint16 || keyType.Kind() == reflect.Uint32 || keyType.Kind() == reflect.Uint64: - return strconv.FormatUint(k.Uint(), 10), nil + // The "commented" marker is emitted at column zero, ahead of any table + // indentation, so that the indentation appears inside the comment + // (e.g. `# key = value`). This matches the historical v2.3 layout. + lineStart := len(e.buf) + if commented { + e.buf = append(e.buf, "# "...) + } + e.writeIndent(indent) + e.buf = e.appendKey(e.buf, ent.key) + e.buf = append(e.buf, " = "...) + + // When tables are not indented, the key is emitted at column zero + // regardless of its nesting depth. Value continuation lines (most + // notably the elements of a multiline array) must line up with that key, + // so the value indentation starts from zero as well rather than from the + // table nesting depth. + valueIndent := indent + if !e.indentTables { + valueIndent = 0 + } - case keyType.Kind() == reflect.Float32: - return strconv.FormatFloat(k.Float(), 'f', -1, 32), nil + // A Marshaler value is delegated, keeping this hot function lean for the + // default path (rawShape stays shapeUnknown when the interface is off). It + // shares the commented/newline handling below. + var err error + if ent.options.rawShape != shapeUnknown { + e.buf, err = e.appendMarshalerInlineValue(e.buf, &ent) + } else { + e.buf, err = e.appendValue(e.buf, ent.value, ent.options, valueIndent) + } + if err != nil { + return err + } - case keyType.Kind() == reflect.Float64: - return strconv.FormatFloat(k.Float(), 'f', -1, 64), nil + // A commented value that renders across multiple lines (a multiline string + // or a multiline array) must have every physical line prefixed with the + // comment marker, not just the first; otherwise the continuation lines are + // emitted as live, syntactically invalid TOML. + if commented { + if bytes.IndexByte(e.buf[lineStart:], '\n') >= 0 { + region := bytes.ReplaceAll( + append([]byte(nil), e.buf[lineStart:]...), + []byte("\n"), []byte("\n# "), + ) + e.buf = append(e.buf[:lineStart], region...) + } } - return "", fmt.Errorf("toml: type %s is not supported as a map key", keyType.Kind()) + e.buf = append(e.buf, '\n') + e.lastWasHeader = false + return nil } -func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { - var ( - t table - emptyValueOptions valueOptions - ) +// appendMarshalerInlineValue appends the value part of a `key = ` line for an +// unstable.Marshaler entry, leaving the trailing newline and commented handling +// to encodeKeyValue. Empty entries are filtered out before this point, so the +// value is either a single value (spliced verbatim) or, when forced inline by +// SetTablesInline or an inline tag, table content (an error). +func (e *encoderState) appendMarshalerInlineValue(b []byte, ent *entry) ([]byte, error) { + errNotValue := func() error { + return fmt.Errorf("toml: cannot encode %s as an inline value: not a single TOML value", ent.value.Type()) + } + if ent.options.rawShape != shapeValue { + return nil, errNotValue() + } + raw, err := e.marshalerValue(ent) + if err != nil { + return nil, err + } + // Classify the just-returned bytes rather than trusting the earlier pass: + // MarshalTOML is called again for the emit, and an implementation that + // returns different content must not splice non-value bytes into a + // `key = ` position. + if shape, trimmed := e.classifyRaw(raw); shape == shapeValue { + return append(b, trimmed...), nil + } + return nil, errNotValue() +} - iter := v.MapRange() - for iter.Next() { - v := iter.Value() +// collectEntries builds the ordered list of the entries of a table, +// applying tags and omission rules. +func (e *encoderState) collectEntries(v reflect.Value) ([]entry, error) { + switch v.Kind() { + case reflect.Map: + return e.collectMapEntries(v) + case reflect.Struct: + entries := e.getEntries() + e.collectStructEntries(&entries, v) + return entries, nil + default: + return nil, fmt.Errorf("toml: cannot encode a %s as a table", v.Type()) + } +} - if isNil(v) { - continue +func (e *encoderState) collectMapEntries(v reflect.Value) ([]entry, error) { + entries := e.getEntries() + + // Keys are converted to strings right away: read them into a reusable + // buffer to avoid one allocation per key. + var kbuf reflect.Value + if v.Type().Key() == stringType { + if !e.stringKeyBuf.IsValid() { + e.stringKeyBuf = reflect.New(stringType).Elem() } + kbuf = e.stringKeyBuf + } else { + kbuf = reflect.New(v.Type().Key()).Elem() + } - k, err := enc.keyToString(iter.Key()) + iter := v.MapRange() + for iter.Next() { + kbuf.SetIterKey(iter) + key, err := mapKeyString(kbuf) if err != nil { return nil, err } - - if willConvertToTableOrArrayTable(ctx, v) { - t.pushTable(k, v, emptyValueOptions) - } else { - t.pushKV(k, v, emptyValueOptions) + value := iter.Value() + if value.Kind() == reflect.Interface && value.IsNil() { + // nil interface values are skipped + continue + } + if value.Kind() == reflect.Ptr && value.IsNil() { + // nil pointers in maps are encoded as their zero value + value = reflect.New(value.Type().Elem()).Elem() } + entries = append(entries, entry{key: key, value: value}) } - sortEntriesByKey(t.kvs) - sortEntriesByKey(t.tables) + if len(entries) > 1 { + // slices.SortFunc avoids boxing the slice into a sort.Interface (an + // allocation that sort.Sort incurs for every table). + slices.SortFunc(entries, func(a, b entry) int { + return strings.Compare(a.key, b.key) + }) + } - return enc.encodeTable(b, ctx, t) + return entries, nil } -func sortEntriesByKey(e []entry) { - slices.SortFunc(e, func(a, b entry) int { - return strings.Compare(a.Key, b.Key) - }) +// mapKeyString converts a map key to its string representation. +func mapKeyString(k reflect.Value) (string, error) { + kr, ok := resolve(k) + if !ok { + return "", errors.New("toml: cannot encode a nil map key") + } + if kr.Type().Implements(textMarshalerType) { + b, err := kr.Interface().(encoding.TextMarshaler).MarshalText() + if err != nil { + return "", fmt.Errorf("toml: cannot marshal map key: %w", err) + } + return string(b), nil + } + if kr.CanAddr() && reflect.PtrTo(kr.Type()).Implements(textMarshalerType) { + b, err := kr.Addr().Interface().(encoding.TextMarshaler).MarshalText() + if err != nil { + return "", fmt.Errorf("toml: cannot marshal map key: %w", err) + } + return string(b), nil + } + switch kr.Kind() { + case reflect.String: + return kr.String(), nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(kr.Int(), 10), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return strconv.FormatUint(kr.Uint(), 10), nil + case reflect.Float32: + return strconv.FormatFloat(kr.Float(), 'f', -1, 32), nil + case reflect.Float64: + return strconv.FormatFloat(kr.Float(), 'f', -1, 64), nil + default: + return "", fmt.Errorf("toml: cannot encode a map with key type %s", k.Type()) + } } -type entry struct { - Key string - Value reflect.Value - Options valueOptions +// encPlanField is the static encoding information of one field of a struct. +type encPlanField struct { + name string + index []int + depth int + options valueOptions } -type table struct { - kvs []entry - tables []entry +// encPlan caches the per-type information needed to encode a struct: +// flattened fields with parsed tags, in order of definition, with shadowed +// duplicates already removed. +type encPlan struct { + fields []encPlanField } -func (t *table) pushKV(k string, v reflect.Value, options valueOptions) { - for _, e := range t.kvs { - if e.Key == k { - return - } - } - - t.kvs = append(t.kvs, entry{Key: k, Value: v, Options: options}) -} +var encPlans sync.Map // reflect.Type -> *encPlan -func (t *table) pushTable(k string, v reflect.Value, options valueOptions) { - for _, e := range t.tables { - if e.Key == k { - return - } +func encPlanForType(t reflect.Type) *encPlan { + if plan, ok := encPlans.Load(t); ok { + return plan.(*encPlan) } - t.tables = append(t.tables, entry{Key: k, Value: v, Options: options}) + plan := &encPlan{} + visited := map[reflect.Type]bool{} + buildEncPlan(plan, t, nil, 0, visited) + dedupEncPlan(plan) + encPlans.Store(t, plan) + return plan } -func walkStruct(ctx encoderCtx, t *table, v reflect.Value) { - // TODO: cache this - typ := v.Type() - for i := 0; i < typ.NumField(); i++ { - fieldType := typ.Field(i) - - // only consider exported fields - if fieldType.PkgPath != "" { - continue - } +func buildEncPlan(plan *encPlan, t reflect.Type, prefix []int, depth int, visited map[reflect.Type]bool) { + if visited[t] { + return + } + visited[t] = true + defer delete(visited, t) - tag := fieldType.Tag.Get("toml") + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) - // special field name to skip field + tag, tagged := f.Tag.Lookup("toml") if tag == "-" { continue } - k, opts := parseTag(tag) - if !isValidName(k) { - k = "" - } - - f := v.Field(i) - - if k == "" { - if fieldType.Anonymous { - if fieldType.Type.Kind() == reflect.Struct { - walkStruct(ctx, t, f) - } else if fieldType.Type.Kind() == reflect.Ptr && !f.IsNil() && f.Elem().Kind() == reflect.Struct { - walkStruct(ctx, t, f.Elem()) + name := f.Name + var opts valueOptions + if tagged { + parts := strings.Split(tag, ",") + if parts[0] != "" { + name = parts[0] + } + for _, opt := range parts[1:] { + switch opt { + case "multiline": + opts.multiline = true + case "inline": + opts.inline = true + case "omitempty": + opts.omitempty = true + case "omitzero": + opts.omitzero = true + case "commented": + opts.commented = true } - continue - } else { - k = fieldType.Name } } - - if isNil(f) { - continue + // Standalone boolean tags, e.g. multiline:"true". + const tagTrue = "true" + if f.Tag.Get("multiline") == tagTrue { + opts.multiline = true } - - options := valueOptions{ - multiline: opts.multiline, - omitempty: opts.omitempty, - commented: opts.commented, - comment: fieldType.Tag.Get("comment"), + if f.Tag.Get("inline") == tagTrue { + opts.inline = true } - - if opts.inline || !willConvertToTableOrArrayTable(ctx, f) { - t.pushKV(k, f, options) - } else { - t.pushTable(k, f, options) + if f.Tag.Get("commented") == tagTrue { + opts.commented = true } - } -} + opts.comment = f.Tag.Get("comment") -func (enc *Encoder) encodeStruct(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { - var t table + index := make([]int, 0, len(prefix)+1) + index = append(index, prefix...) + index = append(index, i) - walkStruct(ctx, &t, v) + if f.Anonymous { + ft := f.Type + if ft.Kind() == reflect.Ptr { + ft = ft.Elem() + } + if ft.Kind() == reflect.Struct && (!tagged || tagName(tag) == "") { + buildEncPlan(plan, ft, index, depth+1, visited) + continue + } + if f.PkgPath != "" && ft.Kind() != reflect.Interface { + continue + } + } else if f.PkgPath != "" { + // unexported + continue + } - return enc.encodeTable(b, ctx, t) + plan.fields = append(plan.fields, encPlanField{ + name: name, + index: index, + depth: depth, + options: opts, + }) + } } -func (enc *Encoder) encodeComment(indent int, comment string, b []byte) []byte { - for len(comment) > 0 { - var line string - idx := strings.IndexByte(comment, '\n') - if idx >= 0 { - line = comment[:idx] - comment = comment[idx+1:] +// dedupEncPlan removes the fields shadowed by another one with the same +// name (the shallowest wins), keeping the order of first appearance. +func dedupEncPlan(plan *encPlan) { + byName := make(map[string]int, len(plan.fields)) + drop := false + for i := range plan.fields { + f := &plan.fields[i] + j, seen := byName[f.name] + if !seen { + byName[f.name] = i + continue + } + drop = true + // Shallowest wins; on equal depth, the first in order wins. + if f.depth < plan.fields[j].depth { + plan.fields[j].name = "" + byName[f.name] = i } else { - line = comment - comment = "" + f.name = "" } - b = enc.indent(indent, b) - b = append(b, "# "...) - b = append(b, line...) - b = append(b, '\n') } - return b -} - -func isValidName(s string) bool { - if s == "" { - return false + if !drop { + return } - for _, c := range s { - switch { - case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c): - // Backslash and quote chars are reserved, but - // otherwise any punctuation chars are allowed - // in a tag name. - case !unicode.IsLetter(c) && !unicode.IsDigit(c): - return false + out := plan.fields[:0] + for _, f := range plan.fields { + if f.name != "" { + out = append(out, f) } } - return true + plan.fields = out } -type tagOptions struct { - multiline bool - inline bool - omitempty bool - commented bool -} - -func parseTag(tag string) (string, tagOptions) { - opts := tagOptions{} - - idx := strings.Index(tag, ",") - if idx == -1 { - return tag, opts - } +// collectStructEntries appends the entries of a struct, flattening embedded +// structs in place. +func (e *encoderState) collectStructEntries(entries *[]entry, v reflect.Value) { + plan := encPlanForType(v.Type()) - raw := tag[idx+1:] - tag = string(tag[:idx]) - for raw != "" { - var o string - i := strings.Index(raw, ",") - if i >= 0 { - o, raw = raw[:i], raw[i+1:] - } else { - o, raw = raw, "" - } - switch o { - case "multiline": - opts.multiline = true - case "inline": - opts.inline = true - case "omitempty": - opts.omitempty = true - case "commented": - opts.commented = true + for i := range plan.fields { + f := &plan.fields[i] + fv, ok := fieldByIndexSkipNil(v, f.index) + if !ok { + // nil embedded pointer on the way: skipped + continue } - } - - return tag, opts -} - -func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, error) { - var err error - - ctx.shiftKey() - - if ctx.insideKv || (ctx.inline && !ctx.isRoot()) { - return enc.encodeTableInline(b, ctx, t) - } - if !ctx.skipTableHeader { - b, err = enc.encodeTableHeader(ctx, b) - if err != nil { - return nil, err + // Anonymous interface fields that are nil are skipped. + if fv.Kind() == reflect.Interface && fv.IsNil() { + continue } - - if enc.indentTables && len(ctx.parentKey) > 0 { - ctx.indent++ + // nil values in struct fields are skipped + if (fv.Kind() == reflect.Ptr || fv.Kind() == reflect.Map) && fv.IsNil() { + continue } - } - ctx.skipTableHeader = false - hasNonEmptyKV := false - for _, kv := range t.kvs { - if shouldOmitEmpty(kv.Options, kv.Value) { + if f.options.omitempty && isEmptyValue(fv) { continue } - hasNonEmptyKV = true - - ctx.setKey(kv.Key) - ctx2 := ctx - ctx2.commented = kv.Options.commented || ctx2.commented - - b, err = enc.encodeKv(b, ctx2, kv.Options, kv.Value) - if err != nil { - return nil, err + if f.options.omitzero && isZeroValue(fv) { + continue } - b = append(b, '\n') + *entries = append(*entries, entry{key: f.name, value: fv, options: f.options}) } +} - first := true - for _, table := range t.tables { - if shouldOmitEmpty(table.Options, table.Value) { - continue - } - if first { - first = false - if hasNonEmptyKV { - b = append(b, '\n') +// fieldByIndexSkipNil returns the field at the given index path, reporting +// false if a nil embedded pointer is found on the way. +func fieldByIndexSkipNil(v reflect.Value, index []int) (reflect.Value, bool) { + for i, x := range index { + if i > 0 { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return v, false + } + v = v.Elem() } - } else { - b = append(b, "\n"...) } + v = v.Field(x) + } + return v, true +} - ctx.setKey(table.Key) - - ctx.options = table.Options - ctx2 := ctx - ctx2.commented = ctx2.commented || ctx.options.commented +func tagName(tag string) string { + if idx := strings.IndexByte(tag, ','); idx >= 0 { + return tag[:idx] + } + return tag +} - b, err = enc.encode(b, ctx2, table.Value) - if err != nil { - return nil, err +// isEmptyValue implements the omitempty rules. +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Map, reflect.Slice, reflect.Array: + return v.Len() == 0 + case reflect.Ptr, reflect.Interface: + return v.IsNil() + case reflect.Struct: + // Structs that encode as a scalar value (time.Time, the local + // date/time types, or any TextMarshaler) are empty only when they + // equal their zero value; their fields are typically unexported, so + // recursing into them would be meaningless. + if encPropsForType(v.Type()).isValue { + return v.IsZero() } + // Plain structs encode as tables and are empty when every field that + // would be encoded is itself empty. This matches the recursive rule + // used before the encoder rewrite and, in particular, treats a + // non-nil but empty map or slice as empty (reflect.Value.IsZero does + // not, which would otherwise emit an empty table header). + return isEmptyStruct(v) + default: + return false } - - return b, nil } -func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte, error) { - var err error - - b = append(b, '{') - - first := true - for _, kv := range t.kvs { - if shouldOmitEmpty(kv.Options, kv.Value) { +// isEmptyStruct reports whether all of a table-valued struct's encodable +// fields are empty per isEmptyValue. It mirrors the field selection done by +// collectStructEntries (embedded flattening, shadowing, and "-" skips) so the +// emptiness decision matches what would actually be encoded. +func isEmptyStruct(v reflect.Value) bool { + plan := encPlanForType(v.Type()) + for i := range plan.fields { + fv, ok := fieldByIndexSkipNil(v, plan.fields[i].index) + if !ok { + // A nil embedded pointer along the path contributes nothing. continue } - - if first { - first = false - } else { - b = append(b, `, `...) - } - - ctx.setKey(kv.Key) - - b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value) - if err != nil { - return nil, err + if !isEmptyValue(fv) { + return false } } + return true +} - if len(t.tables) > 0 { - panic("inline table cannot contain nested tables, only key-values") +// isZeroValue implements the omitzero rules: the type's own IsZero() when +// implemented, the reflect zero value otherwise. +func isZeroValue(v reflect.Value) bool { + if v.Type().Implements(isZeroerType) { + return v.Interface().(isZeroer).IsZero() } - - b = append(b, "}"...) - - return b, nil + if v.CanAddr() && reflect.PtrTo(v.Type()).Implements(isZeroerType) { + return v.Addr().Interface().(isZeroer).IsZero() + } + if !v.CanAddr() && reflect.PtrTo(v.Type()).Implements(isZeroerType) { + tmp := reflect.New(v.Type()) + tmp.Elem().Set(v) + return tmp.Interface().(isZeroer).IsZero() + } + return v.IsZero() } -func willConvertToTable(ctx encoderCtx, v reflect.Value) bool { - if !v.IsValid() { - return false +// appendKey emits a key, quoted only if necessary. +func (e *encoderState) appendKey(b []byte, key string) []byte { + if isBareKey(key) { + return append(b, key...) } - if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PointerTo(v.Type()).Implements(textMarshalerType)) { + return e.appendString(b, key) +} + +func isBareKey(key string) bool { + if len(key) == 0 { return false } - - t := v.Type() - switch t.Kind() { - case reflect.Map, reflect.Struct: - return !ctx.inline - case reflect.Interface: - return willConvertToTable(ctx, v.Elem()) - case reflect.Ptr: - if v.IsNil() { + for _, c := range []byte(key) { + if !isUnquotedKeyByte(c) { return false } - - return willConvertToTable(ctx, v.Elem()) - default: - return false } + return true } -func willConvertToTableOrArrayTable(ctx encoderCtx, v reflect.Value) bool { - if ctx.insideKv { - return false - } +func isUnquotedKeyByte(c byte) bool { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' +} + +// appendValue emits a TOML value. +func (e *encoderState) appendValue(b []byte, v reflect.Value, opts valueOptions, indent int) ([]byte, error) { t := v.Type() - if t.Kind() == reflect.Interface { - return willConvertToTableOrArrayTable(ctx, v.Elem()) + // Special types take precedence over their kind. + switch t { + case timeType: + return v.Interface().(time.Time).AppendFormat(b, "2006-01-02T15:04:05.999999999Z07:00"), nil + case localDateType: + return append(b, v.Interface().(LocalDate).String()...), nil + case localTimeType: + return append(b, v.Interface().(LocalTime).String()...), nil + case localDateTimeType: + return append(b, v.Interface().(LocalDateTime).String()...), nil + case jsonNumberType: + if e.marshalJSONNumbers { + return appendJSONNumber(b, v.Interface().(json.Number)) + } } - if t.Kind() == reflect.Slice || t.Kind() == reflect.Array { - if v.Len() == 0 { - // An empty slice should be a kv = []. - return false + // A Marshaler reached through a value position (an array element or inline + // table member) splices its bytes verbatim. Everything (including the type + // check) lives in a separate method so this hot function keeps its default + // layout: when the opt-in interface is off, only the bool test runs here. + if e.marshalerOn { + if b2, done, err := e.appendMarshalerInline(b, v, t); done { + return b2, err } + } - for i := 0; i < v.Len(); i++ { - t := willConvertToTable(ctx, v.Index(i)) - - if !t { - return false - } + switch encPropsForType(t).text { + case 1: + if t.Kind() != reflect.String { + return e.appendTextMarshaler(b, v.Interface().(encoding.TextMarshaler)) } - - return true + case 2: + if v.CanAddr() { + return e.appendTextMarshaler(b, v.Addr().Interface().(encoding.TextMarshaler)) + } + tmp := reflect.New(t) + tmp.Elem().Set(v) + return e.appendTextMarshaler(b, tmp.Interface().(encoding.TextMarshaler)) } - return willConvertToTable(ctx, v) + switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { + // nil pointers are encoded as the zero value of their element + // type. + return e.appendValue(b, reflect.Zero(t.Elem()), opts, indent) + } + return e.appendValue(b, v.Elem(), opts, indent) + case reflect.Interface: + if v.IsNil() { + return nil, errors.New("toml: cannot encode a nil interface") + } + return e.appendValue(b, v.Elem(), opts, indent) + case reflect.String: + s := v.String() + // The "multiline" option only takes effect when the string actually + // contains a newline. Wrapping a single-line value such as "2" in a + // """...""" block adds noise without improving readability, so it is + // emitted as a regular single-line string instead. + if opts.multiline && strings.IndexByte(s, '\n') >= 0 { + return e.appendMultilineString(b, s), nil + } + return e.appendString(b, s), nil + case reflect.Bool: + if v.Bool() { + return append(b, "true"...), nil + } + return append(b, "false"...), nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.AppendInt(b, v.Int(), 10), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + u := v.Uint() + if u > math.MaxInt64 { + return nil, fmt.Errorf("toml: cannot encode an unsigned integer above math.MaxInt64: %d", u) + } + return strconv.AppendUint(b, u, 10), nil + case reflect.Float32: + return appendFloat(b, v.Float(), 32), nil + case reflect.Float64: + return appendFloat(b, v.Float(), 64), nil + case reflect.Slice, reflect.Array: + return e.appendArray(b, v, opts, indent) + case reflect.Map: + return e.appendInlineTable(b, v, indent) + case reflect.Struct: + return e.appendInlineTable(b, v, indent) + default: + return nil, fmt.Errorf("toml: cannot encode value of type %s", v.Type()) + } } -func (enc *Encoder) encodeSlice(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { - if v.Len() == 0 { - b = append(b, "[]"...) +var jsonNumberType = reflect.TypeOf(json.Number("")) - return b, nil +func appendJSONNumber(b []byte, n json.Number) ([]byte, error) { + if n == "" { + return append(b, '0'), nil } - - if willConvertToTableOrArrayTable(ctx, v) { - return enc.encodeSliceAsArrayTable(b, ctx, v) + if i, err := n.Int64(); err == nil { + return strconv.AppendInt(b, i, 10), nil } - - return enc.encodeSliceAsArray(b, ctx, v) + f, err := n.Float64() + if err != nil { + return nil, fmt.Errorf("toml: cannot encode json.Number %q: %w", string(n), err) + } + return appendFloat(b, f, 64), nil } -// caller should have checked that v is a slice that only contains values that -// encode into tables. -func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { - ctx.shiftKey() - - scratch := make([]byte, 0, 64) - - scratch = enc.commented(ctx.commented, scratch) +func appendFloat(b []byte, f float64, bitSize int) []byte { + switch { + case math.IsNaN(f): + return append(b, "nan"...) + case math.IsInf(f, 1): + return append(b, "inf"...) + case math.IsInf(f, -1): + return append(b, "-inf"...) + } + start := len(b) + b = strconv.AppendFloat(b, f, 'f', -1, bitSize) + // TOML floats must have a fractional part or an exponent. + if !bytes.ContainsAny(b[start:], ".eE") { + b = append(b, ".0"...) + } + return b +} - if enc.indentTables { - scratch = enc.indent(ctx.indent, scratch) +func (e *encoderState) appendTextMarshaler(b []byte, m encoding.TextMarshaler) ([]byte, error) { + text, err := m.MarshalText() + if err != nil { + return nil, fmt.Errorf("toml: error calling MarshalText: %w", err) } + return e.appendString(b, string(text)), nil +} - scratch = append(scratch, "[["...) +// appendArray encodes a slice or array value. +func (e *encoderState) appendArray(b []byte, v reflect.Value, opts valueOptions, indent int) ([]byte, error) { + multiline := opts.multiline || e.arraysMultiline - for i, k := range ctx.parentKey { - if i > 0 { - scratch = append(scratch, '.') + b = append(b, '[') + if multiline && v.Len() > 0 { + for i := 0; i < v.Len(); i++ { + if i > 0 { + b = append(b, ',') + } + b = append(b, '\n') + for j := 0; j <= indent; j++ { + b = append(b, e.indentSymbol...) + } + var err error + b, err = e.appendValue(b, v.Index(i), valueOptions{}, indent+1) + if err != nil { + return nil, err + } + } + b = append(b, '\n') + for j := 0; j < indent; j++ { + b = append(b, e.indentSymbol...) + } + } else { + for i := 0; i < v.Len(); i++ { + if i > 0 { + b = append(b, ", "...) + } + var err error + b, err = e.appendValue(b, v.Index(i), valueOptions{}, indent) + if err != nil { + return nil, err + } } - - scratch = enc.encodeKey(scratch, k) } + return append(b, ']'), nil +} - scratch = append(scratch, "]]\n"...) - ctx.skipTableHeader = true - - b = enc.encodeComment(ctx.indent, ctx.options.comment, b) - - if enc.indentTables { - ctx.indent++ +// appendInlineTable encodes a map or a struct as an inline table. +func (e *encoderState) appendInlineTable(b []byte, v reflect.Value, indent int) ([]byte, error) { + entries, err := e.collectEntries(v) + if err != nil { + return nil, err } - for i := 0; i < v.Len(); i++ { - if i != 0 { - b = append(b, "\n"...) + b = append(b, '{') + for i, ent := range entries { + if i > 0 { + b = append(b, ", "...) } - - b = append(b, scratch...) - - var err error - b, err = enc.encode(b, ctx, v.Index(i)) + b = e.appendKey(b, ent.key) + b = append(b, " = "...) + // multiline strings are not allowed inside inline tables: they + // would break the single-line requirement. + opts := ent.options + opts.multiline = false + b, err = e.appendValue(b, ent.value, opts, indent) if err != nil { return nil, err } } - - return b, nil + e.putEntries(entries) + return append(b, '}'), nil } -func (enc *Encoder) encodeSliceAsArray(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { - multiline := ctx.options.multiline || enc.arraysMultiline - separator := ", " - - b = append(b, '[') - - subCtx := ctx - subCtx.options = valueOptions{} - - if multiline { - separator = ",\n" - - b = append(b, '\n') - - subCtx.indent++ +// appendString encodes a string, using a literal string when possible and a +// basic string otherwise. +func (e *encoderState) appendString(b []byte, s string) []byte { + if canBeLiteral(s) { + b = append(b, '\'') + b = append(b, s...) + return append(b, '\'') } + return appendBasicString(b, s) +} - var err error - first := true - - for i := 0; i < v.Len(); i++ { - if first { - first = false - } else { - b = append(b, separator...) - } - - if multiline { - b = enc.indent(subCtx.indent, b) - } - - b, err = enc.encode(b, subCtx, v.Index(i)) - if err != nil { - return nil, err +// canBeLiteral returns true when the string can be represented as a TOML +// literal string: no control characters, no single quote, no newline. +func canBeLiteral(s string) bool { + for i := 0; i < len(s); i++ { + c := s[i] + if c == '\'' || c == 0x7f || c < 0x20 { + return false } } + return utf8.ValidString(s) +} - if multiline { - b = append(b, '\n') - b = enc.indent(ctx.indent, b) +// appendBasicString encodes a string as a TOML basic (double-quoted) string. +func appendBasicString(b []byte, s string) []byte { + b = append(b, '"') + for i := 0; i < len(s); { + c := s[i] + switch { + case c == '"': + b = append(b, '\\', '"') + i++ + case c == '\\': + b = append(b, '\\', '\\') + i++ + case c == '\b': + b = append(b, '\\', 'b') + i++ + case c == '\f': + b = append(b, '\\', 'f') + i++ + case c == '\n': + b = append(b, '\\', 'n') + i++ + case c == '\r': + b = append(b, '\\', 'r') + i++ + case c == '\t': + b = append(b, '\\', 't') + i++ + case c < 0x20 || c == 0x7f: + b = append(b, fmt.Sprintf("\\u%04X", c)...) + i++ + default: + r, size := utf8.DecodeRuneInString(s[i:]) + if r == utf8.RuneError && size == 1 { + // Replace invalid bytes by the replacement character. + b = append(b, fmt.Sprintf("\\u%04X", c)...) + i++ + continue + } + b = append(b, s[i:i+size]...) + i += size + } } - - b = append(b, ']') - - return b, nil + return append(b, '"') } -func (enc *Encoder) indent(level int, b []byte) []byte { - for i := 0; i < level; i++ { - b = append(b, enc.indentSymbol...) +// appendMultilineString encodes a string as a TOML multi-line basic string. +func appendMultilineString(b []byte, s string) []byte { + b = append(b, `"""`...) + b = append(b, '\n') + for i := 0; i < len(s); { + c := s[i] + switch { + case c == '"': + // Runs of three or more quotes must be escaped. + j := i + for j < len(s) && s[j] == '"' { + j++ + } + if j-i >= 3 { + for ; i < j; i++ { + b = append(b, '\\', '"') + } + } else { + b = append(b, s[i:j]...) + i = j + } + case c == '\\': + b = append(b, '\\', '\\') + i++ + case c == '\n': + b = append(b, '\n') + i++ + case c == '\b': + b = append(b, '\\', 'b') + i++ + case c == '\f': + b = append(b, '\\', 'f') + i++ + case c == '\r': + b = append(b, '\\', 'r') + i++ + case c == '\t': + b = append(b, '\t') + i++ + case c < 0x20 || c == 0x7f: + b = append(b, fmt.Sprintf("\\u%04X", c)...) + i++ + default: + r, size := utf8.DecodeRuneInString(s[i:]) + if r == utf8.RuneError && size == 1 { + b = append(b, fmt.Sprintf("\\u%04X", c)...) + i++ + continue + } + b = append(b, s[i:i+size]...) + i += size + } } + return append(b, `"""`...) +} - return b +func (e *encoderState) appendMultilineString(b []byte, s string) []byte { + return appendMultilineString(b, s) } diff --git a/vendor/github.com/pelletier/go-toml/v2/strict.go b/vendor/github.com/pelletier/go-toml/v2/strict.go index 802e7e4d..b58571df 100644 --- a/vendor/github.com/pelletier/go-toml/v2/strict.go +++ b/vendor/github.com/pelletier/go-toml/v2/strict.go @@ -1,7 +1,6 @@ package toml import ( - "github.com/pelletier/go-toml/v2/internal/danger" "github.com/pelletier/go-toml/v2/internal/tracker" "github.com/pelletier/go-toml/v2/unstable" ) @@ -12,66 +11,63 @@ type strict struct { // Tracks the current key being processed. key tracker.KeyTracker - missing []unstable.ParserError + missing []decodeError } -func (s *strict) EnterTable(node *unstable.Node) { - if !s.Enabled { - return - } - - s.key.UpdateTable(node) +// decodeError is the information needed to materialize a DecodeError once the +// whole document is available. +type decodeError struct { + highlight unstable.Range + key Key + message string } -func (s *strict) EnterArrayTable(node *unstable.Node) { - if !s.Enabled { - return - } - - s.key.UpdateArrayTable(node) -} - -func (s *strict) EnterKeyValue(node *unstable.Node) { - if !s.Enabled { - return - } - - s.key.Push(node) +// Reset clears the state of the tracker so it can be reused for another +// document. +func (s *strict) Reset() { + s.key = tracker.KeyTracker{} + s.missing = s.missing[:0] } -func (s *strict) ExitKeyValue(node *unstable.Node) { +// EnterTable is called when a new table or array table expression starts +// being processed. +func (s *strict) EnterTable(node *unstable.Node) { if !s.Enabled { return } - - s.key.Pop(node) + s.key.UpdateTable(node) } +// MissingTable is called when a table is present in the document but has no +// corresponding field in the target. func (s *strict) MissingTable(node *unstable.Node) { if !s.Enabled { return } - - s.missing = append(s.missing, unstable.ParserError{ - Highlight: keyLocation(node), - Message: "missing table", - Key: s.key.Key(), + s.missing = append(s.missing, decodeError{ + highlight: keyLocation(node), + key: s.key.Key(), + message: "missing table", }) } +// MissingField is called when a key-value is present in the document but has +// no corresponding field in the target. func (s *strict) MissingField(node *unstable.Node) { if !s.Enabled { return } - - s.missing = append(s.missing, unstable.ParserError{ - Highlight: keyLocation(node), - Message: "missing field", - Key: s.key.Key(), + s.key.Push(node) + s.missing = append(s.missing, decodeError{ + highlight: keyLocation(node), + key: s.key.Key(), + message: "unknown field", }) + s.key.Pop(node) } -func (s *strict) Error(doc []byte) error { +// Error returns the cumulated StrictMissingError for the document, or nil. +func (s *strict) Error(document []byte) error { if !s.Enabled || len(s.missing) == 0 { return nil } @@ -81,14 +77,16 @@ func (s *strict) Error(doc []byte) error { } for _, derr := range s.missing { - derr := derr - err.Errors = append(err.Errors, *wrapDecodeError(doc, &derr)) + highlight := document[derr.highlight.Offset : derr.highlight.Offset+derr.highlight.Length] + err.Errors = append(err.Errors, *newDecodeError(document, highlight, derr.key, derr.message)) } return err } -func keyLocation(node *unstable.Node) []byte { +// keyLocation returns the range of the document covering all the parts of +// the key of the given node. +func keyLocation(node *unstable.Node) unstable.Range { k := node.Key() hasOne := k.Next() @@ -96,12 +94,15 @@ func keyLocation(node *unstable.Node) []byte { panic("should not be called with empty key") } - start := k.Node().Data - end := k.Node().Data + start := k.Node().Raw + end := start for k.Next() { - end = k.Node().Data + end = k.Node().Raw } - return danger.BytesRange(start, end) + return unstable.Range{ + Offset: start.Offset, + Length: end.Offset + end.Length - start.Offset, + } } diff --git a/vendor/github.com/pelletier/go-toml/v2/test-go-versions.sh b/vendor/github.com/pelletier/go-toml/v2/test-go-versions.sh new file mode 100644 index 00000000..5fe5c777 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/test-go-versions.sh @@ -0,0 +1,597 @@ +#!/usr/bin/env bash + +set -uo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Go versions to test (1.11 through 1.26) +GO_VERSIONS=( + "1.11" + "1.12" + "1.13" + "1.14" + "1.15" + "1.16" + "1.17" + "1.18" + "1.19" + "1.20" + "1.21" + "1.22" + "1.23" + "1.24" + "1.25" + "1.26" +) + +# Default values +PARALLEL=true +VERBOSE=false +OUTPUT_DIR="test-results" +DOCKER_TIMEOUT="10m" + +usage() { + cat << EOF +Usage: $0 [OPTIONS] [GO_VERSIONS...] + +Test go-toml across multiple Go versions using Docker containers. + +The script reports the lowest continuous supported Go version (where all subsequent +versions pass) and only exits with non-zero status if either of the two most recent +Go versions fail, indicating immediate attention is needed. + +Note: For Go versions < 1.21, the script automatically updates go.mod to match the +target version, but older versions may still fail due to missing standard library +features (e.g., the 'slices' package introduced in Go 1.21). + +OPTIONS: + -h, --help Show this help message + -s, --sequential Run tests sequentially instead of in parallel + -v, --verbose Enable verbose output + -o, --output DIR Output directory for test results (default: test-results) + -t, --timeout TIME Docker timeout for each test (default: 10m) + --list List available Go versions and exit + +ARGUMENTS: + GO_VERSIONS Specific Go versions to test (default: all supported versions) + Examples: 1.21 1.22 1.23 + +EXAMPLES: + $0 # Test all Go versions in parallel + $0 --sequential # Test all Go versions sequentially + $0 1.21 1.22 1.23 # Test specific versions + $0 --verbose --output ./results 1.25 1.26 # Verbose output to custom directory + +EXIT CODES: + 0 Recent Go versions pass (good compatibility) + 1 Recent Go versions fail (needs attention) or script error + +EOF +} + +log() { + echo -e "${BLUE}[$(date +'%H:%M:%S')]${NC} $*" >&2 +} + +log_success() { + echo -e "${GREEN}[$(date +'%H:%M:%S')] ✓${NC} $*" >&2 +} + +log_error() { + echo -e "${RED}[$(date +'%H:%M:%S')] ✗${NC} $*" >&2 +} + +log_warning() { + echo -e "${YELLOW}[$(date +'%H:%M:%S')] ⚠${NC} $*" >&2 +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + usage + exit 0 + ;; + -s|--sequential) + PARALLEL=false + shift + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + -o|--output) + OUTPUT_DIR="$2" + shift 2 + ;; + -t|--timeout) + DOCKER_TIMEOUT="$2" + shift 2 + ;; + --list) + echo "Available Go versions:" + printf '%s\n' "${GO_VERSIONS[@]}" + exit 0 + ;; + -*) + echo "Unknown option: $1" >&2 + usage + exit 1 + ;; + *) + # Remaining arguments are Go versions + break + ;; + esac +done + +# If specific versions provided, use those instead of defaults +if [[ $# -gt 0 ]]; then + GO_VERSIONS=("$@") +fi + +# Validate Go versions +for version in "${GO_VERSIONS[@]}"; do + if ! [[ "$version" =~ ^1\.(1[1-9]|2[0-6])$ ]]; then + log_error "Invalid Go version: $version. Supported versions: 1.11-1.26" + exit 1 + fi +done + +# Check if Docker is available +if ! command -v docker &> /dev/null; then + log_error "Docker is required but not installed or not in PATH" + exit 1 +fi + +# Check if Docker daemon is running +if ! docker info &> /dev/null; then + log_error "Docker daemon is not running" + exit 1 +fi + +# Create output directory +mkdir -p "$OUTPUT_DIR" + +# Function to test a single Go version +test_go_version() { + local go_version="$1" + local container_name="go-toml-test-${go_version}" + local result_file="${OUTPUT_DIR}/go-${go_version}.txt" + local dockerfile_content + + log "Testing Go $go_version..." + + # Create a temporary Dockerfile for this version + # For Go versions < 1.21, we need to update go.mod to match the Go version + local needs_go_mod_update=false + if [[ $(echo "$go_version 1.21" | tr ' ' '\n' | sort -V | head -n1) == "$go_version" && "$go_version" != "1.21" ]]; then + needs_go_mod_update=true + fi + + dockerfile_content="FROM golang:${go_version}-alpine + +# Install git (required for go mod) +RUN apk add --no-cache git + +# Set working directory +WORKDIR /app + +# Copy source code +COPY . ." + + # Add go.mod update step for older Go versions + if [[ "$needs_go_mod_update" == true ]]; then + dockerfile_content="$dockerfile_content + +# Update go.mod to match Go version (required for Go < 1.21) +RUN if [ -f go.mod ]; then sed -i 's/^go [0-9]\\+\\.[0-9]\\+\\(\\.[0-9]\\+\\)\\?/go $go_version/' go.mod; fi + +# Note: Go versions < 1.21 may fail due to missing standard library packages (e.g., slices) +# This is expected for projects that use Go 1.21+ features" + fi + + dockerfile_content="$dockerfile_content + +# Run tests +CMD [\"sh\", \"-c\", \"go version && echo '--- Running go test ./... ---' && go test ./...\"]" + + # Create temporary directory for this test + local temp_dir + temp_dir=$(mktemp -d) + + # Copy source to temp directory (excluding test results and git) + rsync -a --exclude="$OUTPUT_DIR" --exclude=".git" --exclude="*.test" . "$temp_dir/" + + # Create Dockerfile in temp directory + echo "$dockerfile_content" > "$temp_dir/Dockerfile" + + # Build and run container + local exit_code=0 + local output + + if $VERBOSE; then + log "Building Docker image for Go $go_version..." + fi + + # Capture both stdout and stderr, and the exit code + if output=$(cd "$temp_dir" && timeout "$DOCKER_TIMEOUT" docker build -t "$container_name" . 2>&1 && \ + timeout "$DOCKER_TIMEOUT" docker run --rm "$container_name" 2>&1); then + log_success "Go $go_version: PASSED" + echo "PASSED" > "${result_file}.status" + else + exit_code=$? + log_error "Go $go_version: FAILED (exit code: $exit_code)" + echo "FAILED" > "${result_file}.status" + fi + + # Save full output + echo "$output" > "$result_file" + + # Clean up + docker rmi "$container_name" &> /dev/null || true + rm -rf "$temp_dir" + + if $VERBOSE; then + echo "--- Go $go_version output ---" + echo "$output" + echo "--- End Go $go_version output ---" + fi + + return $exit_code +} + +# Function to run tests in parallel +run_parallel() { + local pids=() + local failed_versions=() + + log "Starting parallel tests for ${#GO_VERSIONS[@]} Go versions..." + + # Start all tests in background + for version in "${GO_VERSIONS[@]}"; do + test_go_version "$version" & + pids+=($!) + done + + # Wait for all tests to complete + for i in "${!pids[@]}"; do + local pid=${pids[$i]} + local version=${GO_VERSIONS[$i]} + + if ! wait $pid; then + failed_versions+=("$version") + fi + done + + return ${#failed_versions[@]} +} + +# Function to run tests sequentially +run_sequential() { + local failed_versions=() + + log "Starting sequential tests for ${#GO_VERSIONS[@]} Go versions..." + + for version in "${GO_VERSIONS[@]}"; do + if ! test_go_version "$version"; then + failed_versions+=("$version") + fi + done + + return ${#failed_versions[@]} +} + +# Main execution +main() { + local start_time + start_time=$(date +%s) + + log "Starting Go version compatibility tests..." + log "Testing versions: ${GO_VERSIONS[*]}" + log "Output directory: $OUTPUT_DIR" + log "Parallel execution: $PARALLEL" + + local failed_count + if $PARALLEL; then + run_parallel + failed_count=$? + else + run_sequential + failed_count=$? + fi + + local end_time + end_time=$(date +%s) + local duration=$((end_time - start_time)) + + # Collect results for display + local passed_versions=() + local failed_versions=() + local unknown_versions=() + local passed_count=0 + + for version in "${GO_VERSIONS[@]}"; do + local status_file="${OUTPUT_DIR}/go-${version}.txt.status" + if [[ -f "$status_file" ]]; then + local status + status=$(cat "$status_file") + if [[ "$status" == "PASSED" ]]; then + passed_versions+=("$version") + ((passed_count++)) + else + failed_versions+=("$version") + fi + else + unknown_versions+=("$version") + fi + done + + # Generate summary report + local summary_file="${OUTPUT_DIR}/summary.txt" + { + echo "Go Version Compatibility Test Summary" + echo "=====================================" + echo "Date: $(date)" + echo "Duration: ${duration}s" + echo "Parallel: $PARALLEL" + echo "" + echo "Results:" + + for version in "${GO_VERSIONS[@]}"; do + local status_file="${OUTPUT_DIR}/go-${version}.txt.status" + if [[ -f "$status_file" ]]; then + local status + status=$(cat "$status_file") + if [[ "$status" == "PASSED" ]]; then + echo " Go $version: ✓ PASSED" + else + echo " Go $version: ✗ FAILED" + fi + else + echo " Go $version: ? UNKNOWN (no status file)" + fi + done + + echo "" + echo "Summary: $passed_count/${#GO_VERSIONS[@]} versions passed" + + if [[ $failed_count -gt 0 ]]; then + echo "" + echo "Failed versions details:" + for version in "${failed_versions[@]}"; do + echo "" + echo "--- Go $version (FAILED) ---" + local result_file="${OUTPUT_DIR}/go-${version}.txt" + if [[ -f "$result_file" ]]; then + tail -n 30 "$result_file" + fi + done + fi + } > "$summary_file" + + # Find lowest continuous supported version and check recent versions + local lowest_continuous_version="" + local recent_versions_failed=false + + # Sort versions to ensure proper order + local sorted_versions=() + for version in "${GO_VERSIONS[@]}"; do + sorted_versions+=("$version") + done + # Sort versions numerically (1.11, 1.12, ..., 1.25) + IFS=$'\n' sorted_versions=($(sort -V <<< "${sorted_versions[*]}")) + + # Find lowest continuous supported version (all versions from this point onwards pass) + for version in "${sorted_versions[@]}"; do + local status_file="${OUTPUT_DIR}/go-${version}.txt.status" + local all_subsequent_pass=true + + # Check if this version and all subsequent versions pass + local found_current=false + for check_version in "${sorted_versions[@]}"; do + if [[ "$check_version" == "$version" ]]; then + found_current=true + fi + + if [[ "$found_current" == true ]]; then + local check_status_file="${OUTPUT_DIR}/go-${check_version}.txt.status" + if [[ -f "$check_status_file" ]]; then + local status + status=$(cat "$check_status_file") + if [[ "$status" != "PASSED" ]]; then + all_subsequent_pass=false + break + fi + else + all_subsequent_pass=false + break + fi + fi + done + + if [[ "$all_subsequent_pass" == true ]]; then + lowest_continuous_version="$version" + break + fi + done + + # Check if the two most recent versions failed + local num_versions=${#sorted_versions[@]} + if [[ $num_versions -ge 2 ]]; then + local second_recent="${sorted_versions[$((num_versions-2))]}" + local most_recent="${sorted_versions[$((num_versions-1))]}" + + local second_recent_status_file="${OUTPUT_DIR}/go-${second_recent}.txt.status" + local most_recent_status_file="${OUTPUT_DIR}/go-${most_recent}.txt.status" + + local second_recent_failed=false + local most_recent_failed=false + + if [[ -f "$second_recent_status_file" ]]; then + local status + status=$(cat "$second_recent_status_file") + if [[ "$status" != "PASSED" ]]; then + second_recent_failed=true + fi + else + second_recent_failed=true + fi + + if [[ -f "$most_recent_status_file" ]]; then + local status + status=$(cat "$most_recent_status_file") + if [[ "$status" != "PASSED" ]]; then + most_recent_failed=true + fi + else + most_recent_failed=true + fi + + if [[ "$second_recent_failed" == true || "$most_recent_failed" == true ]]; then + recent_versions_failed=true + fi + elif [[ $num_versions -eq 1 ]]; then + # Only one version tested, check if it's the most recent and failed + local only_version="${sorted_versions[0]}" + local only_status_file="${OUTPUT_DIR}/go-${only_version}.txt.status" + + if [[ -f "$only_status_file" ]]; then + local status + status=$(cat "$only_status_file") + if [[ "$status" != "PASSED" ]]; then + recent_versions_failed=true + fi + else + recent_versions_failed=true + fi + fi + + # Display summary + echo "" + log "Test completed in ${duration}s" + log "Summary report: $summary_file" + + echo "" + echo "========================================" + echo " FINAL RESULTS" + echo "========================================" + echo "" + + # Display passed versions + if [[ ${#passed_versions[@]} -gt 0 ]]; then + log_success "PASSED (${#passed_versions[@]}/${#GO_VERSIONS[@]}):" + # Sort passed versions for display + local sorted_passed=() + for version in "${sorted_versions[@]}"; do + for passed_version in "${passed_versions[@]}"; do + if [[ "$version" == "$passed_version" ]]; then + sorted_passed+=("$version") + break + fi + done + done + for version in "${sorted_passed[@]}"; do + echo -e " ${GREEN}✓${NC} Go $version" + done + echo "" + fi + + # Display failed versions + if [[ ${#failed_versions[@]} -gt 0 ]]; then + log_error "FAILED (${#failed_versions[@]}/${#GO_VERSIONS[@]}):" + # Sort failed versions for display + local sorted_failed=() + for version in "${sorted_versions[@]}"; do + for failed_version in "${failed_versions[@]}"; do + if [[ "$version" == "$failed_version" ]]; then + sorted_failed+=("$version") + break + fi + done + done + for version in "${sorted_failed[@]}"; do + echo -e " ${RED}✗${NC} Go $version" + done + echo "" + + # Show failure details + echo "========================================" + echo " FAILURE DETAILS" + echo "========================================" + echo "" + + for version in "${sorted_failed[@]}"; do + echo -e "${RED}--- Go $version FAILURE LOGS (last 30 lines) ---${NC}" + local result_file="${OUTPUT_DIR}/go-${version}.txt" + if [[ -f "$result_file" ]]; then + tail -n 30 "$result_file" | sed 's/^/ /' + else + echo " No log file found: $result_file" + fi + echo "" + done + fi + + # Display unknown versions + if [[ ${#unknown_versions[@]} -gt 0 ]]; then + log_warning "UNKNOWN (${#unknown_versions[@]}/${#GO_VERSIONS[@]}):" + for version in "${unknown_versions[@]}"; do + echo -e " ${YELLOW}?${NC} Go $version (no status file)" + done + echo "" + fi + + echo "========================================" + echo " COMPATIBILITY SUMMARY" + echo "========================================" + echo "" + + if [[ -n "$lowest_continuous_version" ]]; then + log_success "Lowest continuous supported version: Go $lowest_continuous_version" + echo " (All versions from Go $lowest_continuous_version onwards pass)" + else + log_error "No continuous version support found" + echo " (No version has all subsequent versions passing)" + fi + + echo "" + echo "========================================" + echo "Full detailed logs available in: $OUTPUT_DIR" + echo "========================================" + + # Determine exit code based on recent versions + if [[ "$recent_versions_failed" == true ]]; then + log_error "OVERALL RESULT: Recent Go versions failed - this needs attention!" + if [[ -n "$lowest_continuous_version" ]]; then + echo "Note: Continuous support starts from Go $lowest_continuous_version" + fi + exit 1 + else + log_success "OVERALL RESULT: Recent Go versions pass - compatibility looks good!" + if [[ -n "$lowest_continuous_version" ]]; then + echo "Continuous support starts from Go $lowest_continuous_version" + fi + exit 0 + fi +} + +# Trap to clean up on exit +cleanup() { + # Kill any remaining background processes + jobs -p | xargs -r kill 2>/dev/null || true + + # Clean up any remaining Docker containers + docker ps -q --filter "name=go-toml-test-" | xargs -r docker stop 2>/dev/null || true + docker images -q --filter "reference=go-toml-test-*" | xargs -r docker rmi 2>/dev/null || true +} + +trap cleanup EXIT + +# Run main function +main diff --git a/vendor/github.com/pelletier/go-toml/v2/toml.abnf b/vendor/github.com/pelletier/go-toml/v2/toml.abnf index 473f3749..7647c4be 100644 --- a/vendor/github.com/pelletier/go-toml/v2/toml.abnf +++ b/vendor/github.com/pelletier/go-toml/v2/toml.abnf @@ -36,7 +36,7 @@ newline =/ %x0D.0A ; CRLF comment-start-symbol = %x23 ; # non-ascii = %x80-D7FF / %xE000-10FFFF -non-eol = %x09 / %x20-7F / non-ascii +non-eol = %x09 / %x20-7E / non-ascii comment = comment-start-symbol *non-eol @@ -74,12 +74,14 @@ escape = %x5C ; \ escape-seq-char = %x22 ; " quotation mark U+0022 escape-seq-char =/ %x5C ; \ reverse solidus U+005C escape-seq-char =/ %x62 ; b backspace U+0008 +escape-seq-char =/ %x65 ; e escape U+001B escape-seq-char =/ %x66 ; f form feed U+000C escape-seq-char =/ %x6E ; n line feed U+000A escape-seq-char =/ %x72 ; r carriage return U+000D escape-seq-char =/ %x74 ; t tab U+0009 -escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX -escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX +escape-seq-char =/ %x78 2HEXDIG ; xHH U+00HH +escape-seq-char =/ %x75 4HEXDIG ; uHHHH U+HHHH +escape-seq-char =/ %x55 8HEXDIG ; UHHHHHHHH U+HHHHHHHH ;; Multiline Basic String @@ -174,7 +176,7 @@ time-secfrac = "." 1*DIGIT time-numoffset = ( "+" / "-" ) time-hour ":" time-minute time-offset = "Z" / time-numoffset -partial-time = time-hour ":" time-minute ":" time-second [ time-secfrac ] +partial-time = time-hour ":" time-minute [ ":" time-second [ time-secfrac ] ] full-date = date-fullyear "-" date-month "-" date-mday full-time = partial-time time-offset @@ -221,13 +223,14 @@ std-table-close = ws %x5D ; ] Right square bracket ;; Inline Table -inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close +inline-table = inline-table-open [ inline-table-keyvals ] ws-comment-newline inline-table-close -inline-table-open = %x7B ws ; { -inline-table-close = ws %x7D ; } -inline-table-sep = ws %x2C ws ; , Comma +inline-table-open = %x7B ; { +inline-table-close = %x7D ; } +inline-table-sep = %x2C ; , Comma -inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] +inline-table-keyvals = ws-comment-newline keyval ws-comment-newline inline-table-sep inline-table-keyvals +inline-table-keyvals =/ ws-comment-newline keyval ws-comment-newline [ inline-table-sep ] ;; Array Table diff --git a/vendor/github.com/pelletier/go-toml/v2/types.go b/vendor/github.com/pelletier/go-toml/v2/types.go index 3c6b8fe5..420f1982 100644 --- a/vendor/github.com/pelletier/go-toml/v2/types.go +++ b/vendor/github.com/pelletier/go-toml/v2/types.go @@ -6,9 +6,19 @@ import ( "time" ) -var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() -var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() -var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() -var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}(nil)) -var sliceInterfaceType = reflect.TypeOf([]interface{}(nil)) -var stringType = reflect.TypeOf("") +// isZeroer is used to check whether a value is the zero value for its type, +// as defined by the type itself. +type isZeroer interface { + IsZero() bool +} + +var isZeroerType = reflect.TypeOf(new(isZeroer)).Elem() + +var ( + timeType = reflect.TypeOf(time.Time{}) + textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem() + textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() + mapStringInterfaceType = reflect.TypeOf(map[string]interface{}(nil)) + sliceInterfaceType = reflect.TypeOf([]interface{}(nil)) + stringType = reflect.TypeOf("") +) diff --git a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go index 189be525..d110243f 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go @@ -9,21 +9,70 @@ import ( "reflect" "strconv" "strings" - "sync/atomic" + "sync" "time" - "github.com/pelletier/go-toml/v2/internal/danger" "github.com/pelletier/go-toml/v2/internal/tracker" "github.com/pelletier/go-toml/v2/unstable" ) +// decoderPool recycles decoders (and their internal buffers: parser arena, +// seen-tracker entries, scratch buffers) across calls to Unmarshal and +// Decode. +var decoderPool = sync.Pool{ + New: func() interface{} { return &decoder{} }, +} + +func getDecoder(strictMode, unmarshalerInterface bool) *decoder { + d := decoderPool.Get().(*decoder) + d.reset() + d.strict.Enabled = strictMode + d.unmarshalerInterface = unmarshalerInterface + return d +} + +func putDecoder(d *decoder) { + decoderPool.Put(d) +} + +// reset clears the per-document state of the decoder, keeping the allocated +// buffers for reuse. +func (d *decoder) reset() { + d.seen.Reset() + d.tableKey = d.tableKey[:0] + d.skipUntilTable = false + d.path = d.path[:0] + d.captures = d.captures[:0] + d.captureIdx = -1 + d.segIdx = d.segIdx[:0] + // Reuse the array-table counter slots across documents instead of + // deleting them: a zeroed slot is indistinguishable from an absent one, + // and keeping it alive means setArrayCount does not have to allocate a new + // *int every time the same path reappears. A safety valve bounds the table + // for adversarial inputs that introduce unboundedly many distinct paths. + if len(d.arrayCounts) > 1<<14 { + d.arrayCounts = nil + } else { + for _, p := range d.arrayCounts { + *p = 0 + } + } + d.tableTarget = reflect.Value{} + d.tableTargetValid = false + d.tableFlush = d.tableFlush[:0] + d.tableParentSlot = slotWriter{} + d.keyParts = d.keyParts[:0] + d.strict.Reset() +} + // Unmarshal deserializes a TOML document into a Go value. // // It is a shortcut for Decoder.Decode() with the default options. func Unmarshal(data []byte, v interface{}) error { - d := decoder{} - d.p.Reset(data) - return d.FromParser(v) + d := getDecoder(false, false) + err := d.unmarshal(data, v) + putDecoder(d) + return err } // Decoder reads and decode a TOML document from an input stream. @@ -57,13 +106,18 @@ func (d *Decoder) DisallowUnknownFields() *Decoder { // EnableUnmarshalerInterface allows to enable unmarshaler interface. // -// With this feature enabled, types implementing the unstable/Unmarshaler +// With this feature enabled, types implementing the unstable.Unmarshaler // interface can be decoded from any structure of the document. It allows types // that don't have a straightforward TOML representation to provide their own // decoding logic. // -// Currently, types can only decode from a single value. Tables and array tables -// are not supported. +// The UnmarshalTOML method receives raw TOML bytes: +// - For single values: the raw value bytes (e.g., `"hello"` for a string) +// - For tables: all key-value lines belonging to that table +// - For inline tables/arrays: the raw bytes of the inline structure +// +// The unstable.RawMessage type can be used to capture raw TOML bytes for +// later processing, similar to json.RawMessage. // // *Unstable:* This method does not follow the compatibility guarantees of // semver. It can be changed or removed without a new major version being @@ -120,164 +174,501 @@ func (d *Decoder) Decode(v interface{}) error { return fmt.Errorf("toml: %w", err) } - dec := decoder{ - strict: strict{ - Enabled: d.strict, - }, - unmarshalerInterface: d.unmarshalerInterface, + dec := getDecoder(d.strict, d.unmarshalerInterface) + err = dec.unmarshal(b, v) + putDecoder(dec) + return err +} + +// pathPart is one part of the key path leading to a value. Parts that come +// from the current table header only carry a name; parts that come from the +// key of the current key-value expression also carry the AST node, and their +// name is materialized lazily to avoid allocations. +type pathPart struct { + name string + node *unstable.Node +} + +// bytes returns the raw bytes of the key part. +func (p *pathPart) bytes() []byte { + if p.node != nil { + return p.node.Data + } + return []byte(p.name) +} + +// str returns the key part as a string, possibly allocating. +func (p *pathPart) str() string { + if p.node != nil { + return string(p.node.Data) } - dec.p.Reset(b) + return p.name +} - return dec.FromParser(v) +// rawCapture accumulates the raw bytes fed to a type implementing +// unstable.Unmarshaler for a table target. The target is identified by the +// parts of its key and the array-table indexes in effect when the capture +// was created, so that it can be located again once the whole document has +// been processed (the address of the target may change as slices grow). +type rawCapture struct { + names []string + // indexes[i] is the index to use when reaching a slice or array right + // before consuming names[i]. indexes[len(names)] is the index of the + // element when the target is an element of an array table. -1 when not + // relevant. + indexes []int + buf []byte } type decoder struct { - // Which parser instance in use for this decoding session. p unstable.Parser - // Flag indicating that the current expression is stashed. - // If set to true, calling nextExpr will not actually pull a new expression - // but turn off the flag instead. - stashedExpr bool + // strict mode + strict strict + + // toggles unmarshaler interface + unmarshalerInterface bool - // Skip expressions until a table is found. This is set to true when a - // table could not be created (missing field in map), so all KV expressions - // need to be skipped. + // tracks the duplicate and type consistency of the keys + seen tracker.SeenTracker + + // path of the current table header, as copied strings + tableKey []string + + // true when the expressions under the current table header cannot be + // stored anywhere and should be skipped skipUntilTable bool - // Flag indicating that the current array/slice table should be cleared because - // it is the first encounter of an array table. - clearArrayTable bool + // scratch buffer for the key path of the current expression + path []pathPart + + // raw captures for the unmarshaler interface, in order of first + // appearance. captureIdx is the index of the capture the current table + // belongs to, or -1. + captures []rawCapture + captureIdx int + + // segIdx[i] records the array element index used when traversing a + // slice or array right before consuming the i-th part of the current + // table key. Reset for each table expression. + segIdx []int + + // arrayCounts tracks the number of elements appended to fixed-size + // arrays used as array tables, keyed by the NUL-joined key parts. + // Values are pointer slots so that updating an existing path does not + // allocate a new key string. + arrayCounts map[string]*int + + // Cached target of the current table, so that key-values do not need to + // walk the document structure from the root for every expression. + // tableFlush holds the write-backs to perform when leaving the table + // (for targets reached through map values, which are copies). + // tableParentSlot stores a replacement of the target itself (e.g. a nil + // map that was allocated) into its parent. + tableTarget reflect.Value + tableTargetValid bool + tableFlush []flushOp + tableParentSlot slotWriter + + // strKey is a reusable string value used as map key, so that map + // operations with string keys do not need to allocate a boxed key for + // every access. It must be refreshed with stringMapKey immediately + // before each use: any recursive call may overwrite it. + strKey reflect.Value + + // interned de-duplicates key strings: documents repeat the same keys + // over and over, and the table survives pooling, so repeated decodes + // of similar documents stop allocating key strings altogether. + interned map[string]string + + // pathScratch is the buffer used by joinPath. + pathScratch []byte + + // keyParts is the reusable buffer holding the decoded parts of the key of + // the current expression in the fused generic decode path. + keyParts [][]byte +} - // Tracks position in Go arrays. - // This is used when decoding [[array tables]] into Go arrays. Given array - // tables are separate TOML expression, we need to keep track of where we - // are at in the Go array, as we can't just introspect its size. - arrayIndexes map[reflect.Value]int +// slotWriter remembers how to store a value at some location of the target +// structure. Implemented as a struct instead of a closure to avoid +// allocations. +type slotWriter struct { + kind uint8 // 0: none, 1: slot.Set, 2: m.SetMapIndex(k, ...), 3: m.SetMapIndex(string key ks, ...) + slot reflect.Value + m reflect.Value + k reflect.Value + ks string +} - // Tracks keys that have been seen, with which type. - seen tracker.SeenTracker +func (d *decoder) storeSlot(s *slotWriter, nv reflect.Value) { + switch s.kind { + case 1: + if s.slot.CanSet() { + s.slot.Set(nv) + } + case 2: + s.m.SetMapIndex(s.k, nv) + case 3: + s.m.SetMapIndex(d.stringMapKey(s.ks), nv) + } +} - // Strict mode - strict strict +// flushOp stores val using w when the table is flushed. +type flushOp struct { + w slotWriter + val reflect.Value +} - // Flag that enables/disables unmarshaler interface. - unmarshalerInterface bool +// flushTable performs the pending write-backs of the cached table target, in +// reverse order so that inner copies land before their parents are stored. +func (d *decoder) flushTable() { + for i := len(d.tableFlush) - 1; i >= 0; i-- { + d.storeSlot(&d.tableFlush[i].w, d.tableFlush[i].val) + } + d.tableFlush = d.tableFlush[:0] + d.tableTargetValid = false + d.tableParentSlot = slotWriter{} + d.tableTarget = reflect.Value{} +} - // Current context for the error. - errorContext *errorContext +// intern returns the string corresponding to the given bytes, reusing a +// previous allocation when the same key has been seen before. +func (d *decoder) intern(b []byte) string { + if s, ok := d.interned[string(b)]; ok { // does not allocate + return s + } + if d.interned == nil { + d.interned = make(map[string]string, 64) + } else if len(d.interned) >= 1<<14 { + // Safety valve for adversarial inputs: do not let the table grow + // without bounds. + for k := range d.interned { + delete(d.interned, k) + } + } + s := string(b) + d.interned[s] = s + return s } -type errorContext struct { - Struct reflect.Type - Field []int +// partString returns the name of a path part, interning it when it comes +// from the document. +func (d *decoder) partString(p *pathPart) string { + if p.node != nil { + return d.intern(p.node.Data) + } + return p.name } -func (d *decoder) typeMismatchError(toml string, target reflect.Type) error { - return fmt.Errorf("toml: %s", d.typeMismatchString(toml, target)) +// stringMapKey returns a reflect.Value holding the given string, reusing the +// same allocation every time. The result must be used (the map operation +// performed) before any recursive call, which may overwrite the buffer. +func (d *decoder) stringMapKey(s string) reflect.Value { + if !d.strKey.IsValid() { + d.strKey = reflect.New(stringType).Elem() + } + d.strKey.SetString(s) + return d.strKey } -func (d *decoder) typeMismatchString(toml string, target reflect.Type) string { - if d.errorContext != nil && d.errorContext.Struct != nil { - ctx := d.errorContext - f := ctx.Struct.FieldByIndex(ctx.Field) - return fmt.Sprintf("cannot decode TOML %s into struct field %s.%s of type %s", toml, ctx.Struct, f.Name, f.Type) +// joinPath builds the NUL-joined representation of a key path in the +// decoder's scratch buffer. The result is only valid until the next call. +func (d *decoder) joinPath(parts []string) []byte { + d.pathScratch = d.pathScratch[:0] + for i, p := range parts { + if i > 0 { + d.pathScratch = append(d.pathScratch, 0) + } + d.pathScratch = append(d.pathScratch, p...) } - return fmt.Sprintf("cannot decode TOML %s into a Go value of type %s", toml, target) + return d.pathScratch } -func (d *decoder) expr() *unstable.Node { - return d.p.Expression() +// arrayCount returns the number of elements appended so far to the array +// table at the given path. +func (d *decoder) arrayCount(key []byte) int { + if d.arrayCounts == nil { + return 0 + } + if p := d.arrayCounts[string(key)]; p != nil { // does not allocate + return *p + } + return 0 } -func (d *decoder) nextExpr() bool { - if d.stashedExpr { - d.stashedExpr = false - return true +func (d *decoder) setArrayCount(key []byte, n int) { + if d.arrayCounts == nil { + d.arrayCounts = map[string]*int{} + } + if p := d.arrayCounts[string(key)]; p != nil { // does not allocate + *p = n + return } - return d.p.NextExpression() + v := n + d.arrayCounts[string(key)] = &v } -func (d *decoder) stashExpr() { - d.stashedExpr = true +// resetChildArrayCounts forgets the counts of all the array tables under +// the given path, so that a new element starts fresh. +func (d *decoder) resetChildArrayCounts(key []byte) { + if len(d.arrayCounts) == 0 { + return + } + for k, p := range d.arrayCounts { + // Prefix match without building the prefix string: same bytes as + // key, followed by the NUL separator. + if len(k) > len(key) && k[len(key)] == 0 && k[:len(key)] == string(key) { + // Zero instead of delete: the next element of the parent table + // will reuse the slot without allocating a new key. + *p = 0 + } + } } -func (d *decoder) arrayIndex(shouldAppend bool, v reflect.Value) int { - if d.arrayIndexes == nil { - d.arrayIndexes = make(map[reflect.Value]int, 1) +func (d *decoder) typeMismatchError(toml string, target reflect.Type, highlight []byte) error { + return &typeMismatchError{ + toml: toml, + target: target, + highlight: highlight, } +} - idx, ok := d.arrayIndexes[v] +type typeMismatchError struct { + toml string + target reflect.Type + highlight []byte + // key is the TOML key being processed when the mismatch occurred. It is + // populated lazily as the error propagates back up to the key-value + // handler (see contextualizeError). + key Key +} - if !ok { - d.arrayIndexes[v] = 0 - } else if shouldAppend { - idx++ - d.arrayIndexes[v] = idx +func (e *typeMismatchError) Error() string { + return fmt.Sprintf("cannot decode TOML %s into %s", e.toml, e.target) +} + +// contextualizeError attaches the TOML key currently being processed to errors +// raised while decoding a key-value expression, so that DecodeError.Key() +// reports the offending key (e.g. on type mismatch errors). The current key is +// reconstructed from d.path; when the table target is cached, d.path holds only +// the key-value parts, so the table key prefix is prepended. This only runs on +// the error path and adds no cost to successful decodes. +func (d *decoder) contextualizeError(err error, withTableKey bool) error { + var mm *typeMismatchError + if errors.As(err, &mm) { + if mm.key == nil { + mm.key = d.currentKey(withTableKey) + } + return err + } + var perr *unstable.ParserError + if errors.As(err, &perr) { + if perr.Key == nil { + perr.Key = d.currentKey(withTableKey) + } } + return err +} - return idx +// currentKey reconstructs the full TOML key being processed from the decoder's +// path. When withTableKey is true, d.path contains only the key-value parts +// (the table target is cached) and the table key is prepended. +func (d *decoder) currentKey(withTableKey bool) Key { + n := len(d.path) + if withTableKey { + n += len(d.tableKey) + } + key := make(Key, 0, n) + if withTableKey { + key = append(key, d.tableKey...) + } + for i := range d.path { + key = append(key, d.path[i].str()) + } + return key } -func (d *decoder) FromParser(v interface{}) error { +func (d *decoder) unmarshal(data []byte, v interface{}) error { r := reflect.ValueOf(v) if r.Kind() != reflect.Ptr { return fmt.Errorf("toml: decoding can only be performed into a pointer, not %s", r.Kind()) } - if r.IsNil() { - return fmt.Errorf("toml: decoding pointer target cannot be nil") + return errors.New("toml: decoding pointer target cannot be nil") } - r = r.Elem() - if r.Kind() == reflect.Interface && r.IsNil() { - newMap := map[string]interface{}{} - r.Set(reflect.ValueOf(newMap)) + root := r.Elem() + + d.captureIdx = -1 + d.p.Reset(data) + + // Fully generic targets (interface{} or map[string]interface{}) are + // decoded straight into native Go maps and slices, with no reflection on + // the document structure at all. This covers the common "decode arbitrary + // TOML into a map" case, including every standard benchmark dataset. + if !d.unmarshalerInterface { + if k := root.Kind(); k == reflect.Interface || (k == reflect.Map && root.Type() == mapStringInterfaceType) { + return d.unmarshalFused(root, data) + } } - err := d.fromParser(r) - if err == nil { - return d.strict.Error(d.p.Data()) + // When the root target itself implements the Unmarshaler interface, the + // whole document decodes into it. Open a capture spanning the entire + // document up front: the top-level key-values then flow through the + // existing capture branch and any tables attach to it via resumeCapture, + // so UnmarshalTOML receives the assembled document exactly once. + if d.unmarshalerInterface && hasUnmarshaler(root) { + d.startRootCapture() } - var e *unstable.ParserError - if errors.As(err, &e) { - return wrapDecodeError(d.p.Data(), e) + for d.p.NextExpression() { + err := d.handleRootExpression(d.p.Expression(), root) + if err != nil { + return d.wrapError(data, err) + } + } + if err := d.p.Error(); err != nil { + var perr *unstable.ParserError + if errors.As(err, &perr) { + return wrapDecodeError(data, perr) + } + return err } - return err -} + d.flushTable() -func (d *decoder) fromParser(root reflect.Value) error { - for d.nextExpr() { - err := d.handleRootExpression(d.expr(), root) + // Deliver the accumulated raw documents to the unmarshaler-interface + // targets. + for i := range d.captures { + nv, err := d.resolveCapture(root, &d.captures[i], 0, false) if err != nil { return err } + if nv.IsValid() { + root.Set(nv) + } + } + + // An empty document into a generic target still initializes it. + switch root.Kind() { + case reflect.Map: + if root.IsNil() { + root.Set(reflect.MakeMap(root.Type())) + } + case reflect.Interface: + if root.IsNil() { + root.Set(reflect.ValueOf(map[string]interface{}{})) + } + default: } - return d.p.Error() + return d.strict.Error(data) +} + +// setAnyKey assigns the value of a key-value into the native map m, following +// the (possibly dotted) key and creating intermediate maps as needed. +func (d *decoder) setAnyKey(m map[string]interface{}, key unstable.Iterator, value *unstable.Node) error { + cur := m + for key.Next() { + name := d.intern(key.Node().Data) + if key.IsLast() { + av, err := d.decodeAny(value) + if err != nil { + return err + } + cur[name] = av + return nil + } + cur = d.anyChildTable(cur, name) + } + return nil } -/* -Rules for the unmarshal code: +// anyChildTable returns the child table at name within cur, creating it if +// absent and descending into the current (last) element when an array table +// occupies the slot. A non-container in the slot cannot occur for a document +// the seen-tracker has accepted. +func (d *decoder) anyChildTable(cur map[string]interface{}, name string) map[string]interface{} { + switch v := cur[name].(type) { + case map[string]interface{}: + return v + case []interface{}: + if len(v) > 0 { + if last, ok := v[len(v)-1].(map[string]interface{}); ok { + return last + } + } + } + nm := map[string]interface{}{} + cur[name] = nm + return nm +} -- The stack is used to keep track of which values need to be set where. -- handle* functions <=> switch on a given unstable.Kind. -- unmarshalX* functions need to unmarshal a node of kind X. -- An "object" is either a struct or a map. -*/ +// wrapError gives document context to errors generated while processing an +// expression. +func (d *decoder) wrapError(data []byte, err error) error { + var perr *unstable.ParserError + if errors.As(err, &perr) { + return wrapDecodeError(data, perr) + } + var mm *typeMismatchError + if errors.As(err, &mm) { + return wrapDecodeError(data, &unstable.ParserError{ + Highlight: mm.highlight, + Message: mm.Error(), + Key: mm.key, + }) + } + return err +} -func (d *decoder) handleRootExpression(expr *unstable.Node, v reflect.Value) error { - var x reflect.Value - var err error - var first bool // used for to clear array tables on first use +// keyedError turns a bare error raised while processing an expression into a +// ParserError carrying the position and key of that expression, so that it is +// reported as a DecodeError with context. It is used for the errors returned +// by SeenTracker.CheckExpression (redefinitions and duplicate keys, see issue +// #668) and for table placement errors raised by walkTable (see issue #806). +// +// The highlight spans the expression's key. Unlike Node.Raw, key nodes always +// carry a Raw range, so this works for tables and array tables too (whose own +// Raw range is not set by the parser). For a duplicate detected inside an +// inline table, node is the enclosing key-value expression, so the error +// points at that expression's key. +func (d *decoder) keyedError(node *unstable.Node, err error) error { + if err == nil { + return nil + } - if !(d.skipUntilTable && expr.Kind == unstable.KeyValue) { - first, err = d.seen.CheckExpression(expr) - if err != nil { - return err + var key Key + var start, end unstable.Range + it := node.Key() + for it.Next() { + n := it.Node() + key = append(key, string(n.Data)) + if len(key) == 1 { + start = n.Raw } + end = n.Raw + } + + var highlight []byte + if len(key) > 0 { + highlight = d.p.Raw(unstable.Range{ + Offset: start.Offset, + Length: end.Offset + end.Length - start.Offset, + }) + } + + return &unstable.ParserError{ + Highlight: highlight, + Message: strings.TrimPrefix(err.Error(), "toml: "), + Key: key, + } +} + +func (d *decoder) handleRootExpression(expr *unstable.Node, root reflect.Value) error { + first, err := d.seen.CheckExpression(expr) + if err != nil { + return d.keyedError(expr, err) } switch expr.Kind { @@ -285,1050 +676,1721 @@ func (d *decoder) handleRootExpression(expr *unstable.Node, v reflect.Value) err if d.skipUntilTable { return nil } - x, err = d.handleKeyValue(expr, v) + if d.captureIdx >= 0 { + d.captureKeyValue(expr) + return nil + } + return d.handleKeyValueExpression(expr, root) case unstable.Table: + d.flushTable() d.skipUntilTable = false + d.captureIdx = -1 d.strict.EnterTable(expr) - x, err = d.handleTable(expr.Key(), v) + return d.handleTableExpression(expr, root, false, first) case unstable.ArrayTable: + d.flushTable() d.skipUntilTable = false - d.strict.EnterArrayTable(expr) - d.clearArrayTable = first - x, err = d.handleArrayTable(expr.Key(), v) + d.captureIdx = -1 + d.strict.EnterTable(expr) + return d.handleTableExpression(expr, root, true, first) default: - panic(fmt.Errorf("parser should not permit expression of kind %s at document root", expr.Kind)) + return unstable.NewParserError(expr.Data, "unsupported expression kind %s", expr.Kind) + } +} + +// updateTableKey copies the parts of the key of a table expression into +// tableKey. +func (d *decoder) updateTableKey(expr *unstable.Node) { + d.tableKey = d.tableKey[:0] + it := expr.Key() + for it.Next() { + d.tableKey = append(d.tableKey, d.intern(it.Node().Data)) } +} - if d.skipUntilTable { - if expr.Kind == unstable.Table || expr.Kind == unstable.ArrayTable { - d.strict.MissingTable(expr) +func (d *decoder) handleTableExpression(expr *unstable.Node, root reflect.Value, isArrayTable bool, first bool) error { + d.updateTableKey(expr) + + // Check whether this table belongs to an exisiting raw capture (split + // tables, or children of a table assigned to an Unmarshaler). + if d.unmarshalerInterface { + if d.resumeCapture(expr) { + return nil } - } else if err == nil && x.IsValid() { - v.Set(x) } - return err + // Reset the per-segment array indexes. + d.segIdx = d.segIdx[:0] + for i := 0; i <= len(d.tableKey); i++ { + d.segIdx = append(d.segIdx, -1) + } + + return d.walkTable(root, expr, isArrayTable, first) } -func (d *decoder) handleArrayTable(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { - if key.Next() { - return d.handleArrayTablePart(key, v) +// newContainerElem returns a fresh element for a slice of the given element +// type. Plain interface elements start out as an empty table. +func newContainerElem(et reflect.Type) reflect.Value { + if et == interfaceType { + return reflect.ValueOf(map[string]interface{}{}) } - return d.handleKeyValues(v) + return reflect.New(et).Elem() } -func (d *decoder) handleArrayTableCollectionLast(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { - switch v.Kind() { - case reflect.Interface: - elem := v.Elem() - if !elem.IsValid() { - elem = reflect.New(sliceInterfaceType).Elem() - elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16)) - } else if elem.Kind() == reflect.Slice { - if elem.Type() != sliceInterfaceType { - elem = reflect.New(sliceInterfaceType).Elem() - elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16)) - } else if !elem.CanSet() { - nelem := reflect.New(sliceInterfaceType).Elem() - nelem.Set(reflect.MakeSlice(sliceInterfaceType, elem.Len(), elem.Cap())) - reflect.Copy(nelem, elem) - elem = nelem +// walkTable processes a [table] or [[array table]] header: it creates the +// intermediate containers, appends array-table elements, applies the strict +// policy, registers unmarshaler-interface captures, and caches the target +// container so that the key-values that follow are stored directly. +// +// Map values are not addressable: when one needs in-place mutations (struct +// or array values), a copy is made and registered to be stored back when the +// table changes (see flushTable). Maps and slices are references and are +// traversed without copies. +func (d *decoder) walkTable(root reflect.Value, expr *unstable.Node, isArrayTable bool, first bool) error { + v := root + pf := slotWriter{kind: 1, slot: root} + idx := 0 + +walk: + for { + // Dereference pointers in place. + for v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + elem := v.Elem() + pf = slotWriter{kind: 1, slot: elem} + v = elem + } + + // Tables assigned to a type implementing the unmarshaler interface + // are captured as raw bytes, delivered once the document is read. + if d.unmarshalerInterface && hasUnmarshaler(v) { + d.startCapture(idx, expr) + return nil + } + + if idx >= len(d.tableKey) { + break walk + } + + name := d.tableKey[idx] + + switch v.Kind() { + case reflect.Interface: + if !v.IsNil() { + c := v.Elem() + if k := c.Kind(); k == reflect.Map || k == reflect.Slice { + // Reference types: mutations are visible through the + // existing interface value. + v = c + continue + } + } + // Anything else is replaced by a fresh generic map. + if !mapStringInterfaceType.AssignableTo(v.Type()) { + return d.keyedError(expr, fmt.Errorf("cannot store a table in a %s", v.Type())) + } + fresh := reflect.ValueOf(map[string]interface{}{}) + d.storeSlot(&pf, fresh) + v = fresh + case reflect.Slice: + if v.Len() == 0 { + // Implicit creation of the first element: the array table + // that would create it has not been seen yet (issue 995). + if v.IsNil() { + v = reflect.MakeSlice(v.Type(), 0, 4) + } + v = reflect.Append(v, newContainerElem(v.Type().Elem())) + d.storeSlot(&pf, v) + } + n := v.Len() - 1 + d.segIdx[idx] = n + elem := v.Index(n) + pf = slotWriter{kind: 1, slot: elem} + v = elem + case reflect.Array: + key := d.joinPath(d.tableKey[:idx]) + cnt := d.arrayCount(key) + if cnt == 0 { + cnt = 1 + d.setArrayCount(key, 1) + } + if cnt > v.Len() { + return d.keyedError(expr, fmt.Errorf("cannot reach element %d of array of size %d", cnt-1, v.Len())) } - if d.clearArrayTable && elem.Len() > 0 { - elem.SetLen(0) - d.clearArrayTable = false + d.segIdx[idx] = cnt - 1 + elem := v.Index(cnt - 1) + pf = slotWriter{kind: 1, slot: elem} + v = elem + case reflect.Map: + if v.IsNil() { + nm := reflect.MakeMap(v.Type()) + d.storeSlot(&pf, nm) + v = nm + } + var key reflect.Value + var w slotWriter + if v.Type().Key() == stringType { + key = d.stringMapKey(name) + w = slotWriter{kind: 3, m: v, ks: name} + } else { + k, err := makeMapKey(v.Type().Key(), name) + if err != nil { + return err + } + key = k + w = slotWriter{kind: 2, m: v, k: k} + } + + elem := v.MapIndex(key) + + // The last part of an array table is finalized as a slice + // container: do not materialize a table for it. + if isArrayTable && idx == len(d.tableKey)-1 { + et := v.Type().Elem() + switch et.Kind() { + case reflect.Interface, reflect.Slice, reflect.Array: + if elem.IsValid() { + v = elem + } else { + v = reflect.Zero(et) + } + pf = w + idx++ + continue + default: + } + } + + if elem.IsValid() { + ce := elem + ceIface := false + if ce.Kind() == reflect.Interface { + ceIface = true + if !ce.IsNil() { + ce = ce.Elem() + } + } + switch ce.Kind() { + case reflect.Map, reflect.Slice: + pf = w + v = ce + case reflect.Ptr: + if ce.IsNil() { + np := reflect.New(ce.Type().Elem()) + d.storeSlot(&w, np) + ce = np + } + pf = w + v = ce + case reflect.Struct, reflect.Array: + if ceIface { + // Interface-held non-generic content is replaced. + fresh := reflect.ValueOf(map[string]interface{}{}) + d.storeSlot(&w, fresh) + pf = w + v = fresh + } else { + tmp := reflect.New(elem.Type()).Elem() + tmp.Set(elem) + d.tableFlush = append(d.tableFlush, flushOp{w: w, val: tmp}) + pf = slotWriter{kind: 1, slot: tmp} + v = tmp + } + default: + if !ceIface { + return d.keyedError(expr, fmt.Errorf("cannot store a table in a %s", ce.Type())) + } + fresh := reflect.ValueOf(map[string]interface{}{}) + d.storeSlot(&w, fresh) + pf = w + v = fresh + } + } else { + et := v.Type().Elem() + switch et.Kind() { + case reflect.Interface: + if !mapStringInterfaceType.AssignableTo(et) { + return d.keyedError(expr, fmt.Errorf("cannot store a table in a %s", et)) + } + fresh := reflect.ValueOf(map[string]interface{}{}) + d.storeSlot(&w, fresh) + pf = w + v = fresh + case reflect.Map: + nm := reflect.MakeMap(et) + d.storeSlot(&w, nm) + pf = w + v = nm + case reflect.Ptr: + np := reflect.New(et.Elem()) + d.storeSlot(&w, np) + pf = w + v = np + case reflect.Struct, reflect.Array, reflect.Slice: + tmp := reflect.New(et).Elem() + d.tableFlush = append(d.tableFlush, flushOp{w: w, val: tmp}) + pf = slotWriter{kind: 1, slot: tmp} + v = tmp + default: + return d.keyedError(expr, fmt.Errorf("cannot store a table in a %s", et)) + } } + idx++ + case reflect.Struct: + plan := planForType(v.Type()) + f, found := plan.lookup(name) + if !found { + d.strict.MissingTable(expr) + d.skipUntilTable = true + return nil + } + fv, err := fieldByIndexAlloc(v, f.index) + if err != nil { + return err + } + pf = slotWriter{kind: 1, slot: fv} + v = fv + idx++ + default: + return d.keyedError(expr, fmt.Errorf("cannot store a table in a %s", v.Kind())) } - return d.handleArrayTableCollectionLast(key, elem) - case reflect.Ptr: - elem := v.Elem() - if !elem.IsValid() { - ptr := reflect.New(v.Type().Elem()) - v.Set(ptr) - elem = ptr.Elem() + } + + if isArrayTable { + akey := d.joinPath(d.tableKey) + d.resetChildArrayCounts(akey) + + // Unwrap an interface container. + if v.Kind() == reflect.Interface { + var slice []interface{} + if !v.IsNil() { + if s, ok := v.Elem().Interface().([]interface{}); ok { + slice = s + } + } + if first { + slice = slice[:0] + } + m := map[string]interface{}{} + slice = append(slice, m) + sv := reflect.ValueOf(slice) + d.storeSlot(&pf, sv) + d.setArrayCount(akey, len(slice)) + d.segIdx[len(d.tableKey)] = len(slice) - 1 + d.tableTarget = reflect.ValueOf(m) + d.tableParentSlot = slotWriter{kind: 1, slot: sv.Index(len(slice) - 1)} + d.tableTargetValid = true + return nil } - elem, err := d.handleArrayTableCollectionLast(key, elem) - if err != nil { - return reflect.Value{}, err + switch v.Kind() { + case reflect.Slice: + if v.IsNil() { + v = reflect.MakeSlice(v.Type(), 0, 4) + } else if first { + v = v.Slice(0, 0) + } + v = reflect.Append(v, newContainerElem(v.Type().Elem())) + d.storeSlot(&pf, v) + n := v.Len() - 1 + d.setArrayCount(akey, n+1) + d.segIdx[len(d.tableKey)] = n + elem := v.Index(n) + if d.unmarshalerInterface && hasUnmarshaler(elem) { + d.startCapture(len(d.tableKey), expr) + return nil + } + pf = slotWriter{kind: 1, slot: elem} + v = elem + case reflect.Array: + cnt := d.arrayCount(akey) + if first { + cnt = 0 + } + if cnt >= v.Len() { + return d.keyedError(expr, fmt.Errorf("array of size %d is too small to store this array table", v.Len())) + } + v.Index(cnt).Set(reflect.Zero(v.Type().Elem())) + d.setArrayCount(akey, cnt+1) + d.segIdx[len(d.tableKey)] = cnt + elem := v.Index(cnt) + if d.unmarshalerInterface && hasUnmarshaler(elem) { + d.startCapture(len(d.tableKey), expr) + return nil + } + pf = slotWriter{kind: 1, slot: elem} + v = elem + default: + return d.keyedError(expr, fmt.Errorf("cannot store an array table in a %s", v.Kind())) + } + } + + // Settle on the concrete container for the key-values that follow. + for { + switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { + if !v.CanSet() { + return nil + } + v.Set(reflect.New(v.Type().Elem())) + } + elem := v.Elem() + pf = slotWriter{kind: 1, slot: elem} + v = elem + continue + case reflect.Interface: + if !v.IsNil() { + c := v.Elem() + if c.Type() == mapStringInterfaceType || c.Type() == sliceInterfaceType { + v = c + continue + } + } + if !mapStringInterfaceType.AssignableTo(v.Type()) { + return d.keyedError(expr, fmt.Errorf("cannot store a table in a %s", v.Type())) + } + fresh := reflect.ValueOf(map[string]interface{}{}) + d.storeSlot(&pf, fresh) + v = fresh + continue + case reflect.Slice: + if v.Len() == 0 { + if v.IsNil() { + v = reflect.MakeSlice(v.Type(), 0, 4) + } + v = reflect.Append(v, newContainerElem(v.Type().Elem())) + d.storeSlot(&pf, v) + } + n := v.Len() - 1 + d.segIdx[len(d.tableKey)] = n + elem := v.Index(n) + pf = slotWriter{kind: 1, slot: elem} + v = elem + continue + case reflect.Map, reflect.Struct: + d.tableTarget = v + d.tableParentSlot = pf + d.tableTargetValid = true + return nil + default: + return d.keyedError(expr, fmt.Errorf("cannot store a table in a %s", v.Kind())) } - v.Elem().Set(elem) + } +} - return v, nil - case reflect.Slice: - if d.clearArrayTable && v.Len() > 0 { - v.SetLen(0) - d.clearArrayTable = false +// resumeCapture looks for an existing capture this table expression belongs +// to. It returns true if the expression was consumed. +func (d *decoder) resumeCapture(expr *unstable.Node) bool { + // Iterate in reverse, so that tables attach to the latest element of + // array tables. + for i := len(d.captures) - 1; i >= 0; i-- { + c := &d.captures[i] + if len(d.tableKey) < len(c.names) { + continue } - elemType := v.Type().Elem() - var elem reflect.Value - if elemType.Kind() == reflect.Interface { - elem = makeMapStringInterface() - } else { - elem = reflect.New(elemType).Elem() + if expr.Kind == unstable.ArrayTable && len(d.tableKey) == len(c.names) { + // A new element of an array table is not part of the capture of + // the previous element. + continue } - elem2, err := d.handleArrayTable(key, elem) - if err != nil { - return reflect.Value{}, err + match := true + for j, p := range c.names { + if d.tableKey[j] != p { + match = false + break + } } - if elem2.IsValid() { - elem = elem2 + if !match { + continue } - return reflect.Append(v, elem), nil - case reflect.Array: - idx := d.arrayIndex(true, v) - if idx >= v.Len() { - return v, fmt.Errorf("%s at position %d", d.typeMismatchError("array table", v.Type()), idx) + d.captureIdx = i + if len(d.tableKey) > len(c.names) { + d.appendCaptureHeader(c, expr, len(c.names)) } - elem := v.Index(idx) - _, err := d.handleArrayTable(key, elem) - return v, err - default: - return reflect.Value{}, d.typeMismatchError("array table", v.Type()) + return true } + return false } -// When parsing an array table expression, each part of the key needs to be -// evaluated like a normal key, but if it returns a collection, it also needs to -// point to the last element of the collection. Unless it is the last part of -// the key, then it needs to create a new element at the end. -func (d *decoder) handleArrayTableCollection(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { - if key.IsLast() { - return d.handleArrayTableCollectionLast(key, v) +// appendCaptureHeader writes the table header of expr in the capture buffer, +// adjusted to be relative to the capture root. +func (d *decoder) appendCaptureHeader(c *rawCapture, expr *unstable.Node, skip int) { + c.buf = append(c.buf, '[') + if expr.Kind == unstable.ArrayTable { + c.buf = append(c.buf, '[') } + c.buf = append(c.buf, d.rawKeySuffix(expr, skip)...) + c.buf = append(c.buf, ']') + if expr.Kind == unstable.ArrayTable { + c.buf = append(c.buf, ']') + } + c.buf = append(c.buf, '\n') +} - switch v.Kind() { - case reflect.Ptr: - elem := v.Elem() - if !elem.IsValid() { - ptr := reflect.New(v.Type().Elem()) - v.Set(ptr) - elem = ptr.Elem() +// rawKeySuffix returns the raw bytes of the key of the expression, skipping +// the first n parts. +func (d *decoder) rawKeySuffix(expr *unstable.Node, n int) []byte { + it := expr.Key() + idx := 0 + var start, end unstable.Range + for it.Next() { + if idx >= n { + r := it.Node().Raw + if start.Length == 0 && start.Offset == 0 && idx == n { + start = r + } + end = r } + idx++ + } + return d.p.Data()[start.Offset : end.Offset+end.Length] +} - elem, err := d.handleArrayTableCollection(key, elem) +// startCapture registers a new capture for the table at the given path +// (prefix of tableKey). +func (d *decoder) startCapture(pathLen int, expr *unstable.Node) { + names := make([]string, pathLen) + copy(names, d.tableKey[:pathLen]) + indexes := make([]int, pathLen+1) + copy(indexes, d.segIdx[:pathLen+1]) + d.captures = append(d.captures, rawCapture{ + names: names, + indexes: indexes, + }) + d.captureIdx = len(d.captures) - 1 + if pathLen < len(d.tableKey) { + d.appendCaptureHeader(&d.captures[d.captureIdx], expr, pathLen) + } +} + +// startRootCapture opens a capture covering the entire document, used when the +// root target itself implements the Unmarshaler interface. It is opened before +// reading any expression: top-level key-values are then accumulated through the +// regular capture branch, and its empty name path matches every table in +// resumeCapture, so the whole document is handed to UnmarshalTOML once. The +// single index slot is -1 because the root is never reached through an array +// table. +func (d *decoder) startRootCapture() { + d.captures = append(d.captures, rawCapture{indexes: []int{-1}}) + d.captureIdx = len(d.captures) - 1 +} + +// resolveCapture walks back to the target of a capture and delivers the +// accumulated raw bytes to its UnmarshalTOML implementation. +func (d *decoder) resolveCapture(v reflect.Value, c *rawCapture, idx int, indexed bool) (reflect.Value, error) { + if v.Kind() == reflect.Ptr { + if v.Type().Implements(unmarshalerType) && idx == len(c.names) { + u, _ := unmarshalerOf(v) + return v, u.UnmarshalTOML(c.buf) + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + nv, err := d.resolveCapture(v.Elem(), c, idx, indexed) if err != nil { return reflect.Value{}, err } - if elem.IsValid() { - v.Elem().Set(elem) + if nv.IsValid() { + v.Elem().Set(nv) } - return v, nil - case reflect.Slice: - elem := v.Index(v.Len() - 1) - x, err := d.handleArrayTable(key, elem) - if err != nil || d.skipUntilTable { + } + + if !indexed && (v.Kind() == reflect.Slice || v.Kind() == reflect.Array) && c.indexes[idx] >= 0 { + i := c.indexes[idx] + if i >= v.Len() { + return reflect.Value{}, errors.New("toml: internal error: capture index out of range") + } + elem := v.Index(i) + nv, err := d.resolveCapture(elem, c, idx, true) + if err != nil { return reflect.Value{}, err } - if x.IsValid() { - elem.Set(x) + if nv.IsValid() { + elem.Set(nv) } + return v, nil + } - return v, err - case reflect.Array: - idx := d.arrayIndex(false, v) - if idx >= v.Len() { - return v, fmt.Errorf("%s at position %d", d.typeMismatchError("array table", v.Type()), idx) + if idx == len(c.names) { + u, ok := unmarshalerOf(v) + if !ok { + return reflect.Value{}, errors.New("toml: internal error: capture target does not implement UnmarshalTOML") } - elem := v.Index(idx) - _, err := d.handleArrayTable(key, elem) - return v, err + return v, u.UnmarshalTOML(c.buf) } - return d.handleArrayTable(key, v) -} + name := c.names[idx] -func (d *decoder) handleKeyPart(key unstable.Iterator, v reflect.Value, nextFn handlerFn, makeFn valueMakerFn) (reflect.Value, error) { - var rv reflect.Value - - // First, dispatch over v to make sure it is a valid object. - // There is no guarantee over what it could be. switch v.Kind() { - case reflect.Ptr: - elem := v.Elem() - if !elem.IsValid() { - v.Set(reflect.New(v.Type().Elem())) + case reflect.Struct: + plan := planForType(v.Type()) + f, found := plan.lookup(name) + if !found { + return v, nil } - elem = v.Elem() - return d.handleKeyPart(key, elem, nextFn, makeFn) - case reflect.Map: - vt := v.Type() - - // Create the key for the map element. Convert to key type. - mk, err := d.keyFromData(vt.Key(), key.Node().Data) + fv, err := fieldByIndexAlloc(v, f.index) if err != nil { return reflect.Value{}, err } - - // If the map does not exist, create it. - if v.IsNil() { - vt := v.Type() - v = reflect.MakeMap(vt) - rv = v - } - - mv := v.MapIndex(mk) - set := false - if !mv.IsValid() { - // If there is no value in the map, create a new one according to - // the map type. If the element type is interface, create either a - // map[string]interface{} or a []interface{} depending on whether - // this is the last part of the array table key. - - t := vt.Elem() - if t.Kind() == reflect.Interface { - mv = makeFn() - } else { - mv = reflect.New(t).Elem() - } - set = true - } else if mv.Kind() == reflect.Interface { - mv = mv.Elem() - if !mv.IsValid() { - mv = makeFn() - } - set = true - } else if !mv.CanAddr() { - vt := v.Type() - t := vt.Elem() - oldmv := mv - mv = reflect.New(t).Elem() - mv.Set(oldmv) - set = true - } - - x, err := nextFn(key, mv) + nv, err := d.resolveCapture(fv, c, idx+1, false) if err != nil { return reflect.Value{}, err } - - if x.IsValid() { - mv = x - set = true + if nv.IsValid() && fv.CanSet() { + fv.Set(nv) } - - if set { - v.SetMapIndex(mk, mv) + return v, nil + case reflect.Map: + key, err := makeMapKey(v.Type().Key(), name) + if err != nil { + return reflect.Value{}, err } - case reflect.Struct: - path, found := structFieldPath(v, string(key.Node().Data)) - if !found { - d.skipUntilTable = true - return reflect.Value{}, nil + if v.IsNil() { + v = reflect.MakeMap(v.Type()) } - - if d.errorContext == nil { - d.errorContext = new(errorContext) + elem := reflect.New(v.Type().Elem()).Elem() + if existing := v.MapIndex(key); existing.IsValid() { + elem.Set(existing) } - t := v.Type() - d.errorContext.Struct = t - d.errorContext.Field = path - - f := fieldByIndex(v, path) - x, err := nextFn(key, f) - if err != nil || d.skipUntilTable { + nv, err := d.resolveCapture(elem, c, idx+1, false) + if err != nil { return reflect.Value{}, err } - if x.IsValid() { - f.Set(x) + if nv.IsValid() { + v.SetMapIndex(key, nv) } - d.errorContext.Field = nil - d.errorContext.Struct = nil + return v, nil case reflect.Interface: - if v.Elem().IsValid() { - v = v.Elem() - } else { - v = makeMapStringInterface() - } - - x, err := d.handleKeyPart(key, v, nextFn, makeFn) - if err != nil { + elem := elemOrNewMap(v) + nv, err := d.resolveCapture(elem, c, idx, indexed) + if err != nil || !nv.IsValid() { return reflect.Value{}, err } - if x.IsValid() { - v = x - } - rv = v + return nv, nil default: - panic(fmt.Errorf("unhandled part: %s", v.Kind())) + return reflect.Value{}, fmt.Errorf("toml: internal error: cannot resolve capture target through %s", v.Kind()) } +} - return rv, nil +// captureKeyValue appends the raw bytes of a key-value expression to the +// current capture. +func (d *decoder) captureKeyValue(expr *unstable.Node) { + c := &d.captures[d.captureIdx] + c.buf = append(c.buf, d.p.Raw(expr.Raw)...) + c.buf = append(c.buf, '\n') } -// HandleArrayTablePart navigates the Go structure v using the key v. It is -// only used for the prefix (non-last) parts of an array-table. When -// encountering a collection, it should go to the last element. -func (d *decoder) handleArrayTablePart(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { - var makeFn valueMakerFn - if key.IsLast() { - makeFn = makeSliceInterface - } else { - makeFn = makeMapStringInterface - } - return d.handleKeyPart(key, v, d.handleArrayTableCollection, makeFn) +// hasUnmarshaler reports whether v can provide an unstable.Unmarshaler, +// without allocating anything. +func hasUnmarshaler(v reflect.Value) bool { + t := v.Type() + return t.Implements(unmarshalerType) || (v.CanAddr() && reflect.PtrTo(t).Implements(unmarshalerType)) } -// HandleTable returns a reference when it has checked the next expression but -// cannot handle it. -func (d *decoder) handleTable(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { - if v.Kind() == reflect.Slice { - if v.Len() == 0 { - return reflect.Value{}, unstable.NewParserError(key.Node().Data, "cannot store a table in a slice") - } - elem := v.Index(v.Len() - 1) - x, err := d.handleTable(key, elem) +// makeMapKey converts a TOML key into a value usable as the given map key +// type. +func makeMapKey(kt reflect.Type, name string) (reflect.Value, error) { + switch kt.Kind() { + case reflect.String: + return reflect.ValueOf(name).Convert(kt), nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i, err := strconv.ParseInt(name, 10, 64) if err != nil { - return reflect.Value{}, err + return reflect.Value{}, fmt.Errorf("toml: cannot parse map key %q as %s: %w", name, kt, err) } - if x.IsValid() { - elem.Set(x) - } - return reflect.Value{}, nil - } - if key.Next() { - // Still scoping the key - return d.handleTablePart(key, v) - } - // Done scoping the key. - // Now handle all the key-value expressions in this table. - return d.handleKeyValues(v) -} - -// Handle root expressions until the end of the document or the next -// non-key-value. -func (d *decoder) handleKeyValues(v reflect.Value) (reflect.Value, error) { - var rv reflect.Value - for d.nextExpr() { - expr := d.expr() - if expr.Kind != unstable.KeyValue { - // Stash the expression so that fromParser can just loop and use - // the right handler. - // We could just recurse ourselves here, but at least this gives a - // chance to pop the stack a bit. - d.stashExpr() - break + k := reflect.New(kt).Elem() + if k.OverflowInt(i) { + return reflect.Value{}, fmt.Errorf("toml: map key %q overflows %s", name, kt) } - - _, err := d.seen.CheckExpression(expr) + k.SetInt(i) + return k, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + u, err := strconv.ParseUint(name, 10, 64) if err != nil { - return reflect.Value{}, err + return reflect.Value{}, fmt.Errorf("toml: cannot parse map key %q as %s: %w", name, kt, err) } - - x, err := d.handleKeyValue(expr, v) + k := reflect.New(kt).Elem() + if k.OverflowUint(u) { + return reflect.Value{}, fmt.Errorf("toml: map key %q overflows %s", name, kt) + } + k.SetUint(u) + return k, nil + case reflect.Float32, reflect.Float64: + f, err := strconv.ParseFloat(name, 64) if err != nil { - return reflect.Value{}, err + return reflect.Value{}, fmt.Errorf("toml: cannot parse map key %q as %s: %w", name, kt, err) + } + k := reflect.New(kt).Elem() + k.SetFloat(f) + return k, nil + case reflect.Ptr: + if kt.Implements(textUnmarshalerType) { + k := reflect.New(kt.Elem()) + err := k.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(name)) + if err != nil { + return reflect.Value{}, fmt.Errorf("toml: error unmarshaling map key %q: %w", name, err) + } + return k, nil } - if x.IsValid() { - v = x - rv = x + default: + if reflect.PtrTo(kt).Implements(textUnmarshalerType) { + k := reflect.New(kt) + err := k.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(name)) + if err != nil { + return reflect.Value{}, fmt.Errorf("toml: error unmarshaling map key %q: %w", name, err) + } + return k.Elem(), nil } } - return rv, nil + return reflect.Value{}, fmt.Errorf("toml: cannot decode a key into a map with key type %s", kt) } -type ( - handlerFn func(key unstable.Iterator, v reflect.Value) (reflect.Value, error) - valueMakerFn func() reflect.Value -) - -func makeMapStringInterface() reflect.Value { - return reflect.MakeMap(mapStringInterfaceType) +// elemOrNewMap unwraps an interface value to descend into it. Contents that +// can hold a table (generic maps and slices) are kept; anything else is +// replaced by a fresh map[string]interface{}. Maps and slices are reference +// types: they are returned directly, not copied. +func elemOrNewMap(v reflect.Value) reflect.Value { + if !v.IsNil() { + concrete := v.Elem() + t := concrete.Type() + if t == mapStringInterfaceType || t == sliceInterfaceType { + return concrete + } + } + return reflect.ValueOf(map[string]interface{}{}) } -func makeSliceInterface() reflect.Value { - return reflect.MakeSlice(sliceInterfaceType, 0, 16) -} +// handleKeyValueExpression stores the value of a top-level key-value +// expression, relative to the current table. +func (d *decoder) handleKeyValueExpression(expr *unstable.Node, root reflect.Value) error { + d.path = d.path[:0] -func (d *decoder) handleTablePart(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { - return d.handleKeyPart(key, v, d.handleTable, makeMapStringInterface) -} + target := root + useCache := d.tableTargetValid && len(d.tableKey) > 0 + if useCache { + target = d.tableTarget + } else { + for _, name := range d.tableKey { + d.path = append(d.path, pathPart{name: name}) + } + } -func (d *decoder) tryTextUnmarshaler(node *unstable.Node, v reflect.Value) (bool, error) { - // Special case for time, because we allow to unmarshal to it from - // different kind of AST nodes. - if v.Type() == timeType { - return false, nil + it := expr.Key() + for it.Next() { + d.path = append(d.path, pathPart{node: it.Node()}) } - if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) { - err := v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data) - if err != nil { - return false, unstable.NewParserError(d.p.Raw(node.Raw), "%w", err) + nv, err := d.descend(target, d.path, 0, expr, expr.Value()) + if err != nil { + return d.contextualizeError(err, useCache) + } + if !nv.IsValid() { + return nil + } + if useCache { + // The target may have been replaced (e.g. a nil map allocated): + // re-link it into its parent. + if nv.Kind() == reflect.Map && nv.Pointer() != d.tableTarget.Pointer() { + d.storeSlot(&d.tableParentSlot, nv) + d.tableTarget = nv + } + } else { + if root.CanSet() { + root.Set(nv) } - - return true, nil } - - return false, nil + return nil } -func (d *decoder) handleValue(value *unstable.Node, v reflect.Value) error { - for v.Kind() == reflect.Ptr { - v = initAndDereferencePointer(v) +// descend walks the given key path into v, and assigns the value at the +// end. It returns the value to store back at this level. An invalid value +// means nothing should be stored (e.g. unknown field). +func (d *decoder) descend(v reflect.Value, path []pathPart, idx int, expr *unstable.Node, value *unstable.Node) (reflect.Value, error) { + if idx == len(path) { + return d.assignValue(v, expr, value) } - if d.unmarshalerInterface { - if v.CanAddr() && v.Addr().CanInterface() { - if outi, ok := v.Addr().Interface().(unstable.Unmarshaler); ok { - return outi.UnmarshalTOML(value) - } + if v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + nv, err := d.descend(v.Elem(), path, idx, expr, value) + if err != nil || !nv.IsValid() { + return reflect.Value{}, err } + v.Elem().Set(nv) + return v, nil } - ok, err := d.tryTextUnmarshaler(value, v) - if ok || err != nil { - return err + // A target implementing the unmarshaler interface consumes the value, + // whatever the remaining parts of the key are. + if d.unmarshalerInterface { + if u, ok := unmarshalerOf(v); ok { + return v, u.UnmarshalTOML(d.rawValue(expr, value)) + } } - switch value.Kind { - case unstable.String: - return d.unmarshalString(value, v) - case unstable.Integer: - return d.unmarshalInteger(value, v) - case unstable.Float: - return d.unmarshalFloat(value, v) - case unstable.Bool: - return d.unmarshalBool(value, v) - case unstable.DateTime: - return d.unmarshalDateTime(value, v) - case unstable.LocalDate: - return d.unmarshalLocalDate(value, v) - case unstable.LocalTime: - return d.unmarshalLocalTime(value, v) - case unstable.LocalDateTime: - return d.unmarshalLocalDateTime(value, v) - case unstable.InlineTable: - return d.unmarshalInlineTable(value, v) - case unstable.Array: - return d.unmarshalArray(value, v) - default: - panic(fmt.Errorf("handleValue not implemented for %s", value.Kind)) - } -} + part := path[idx] -func (d *decoder) unmarshalArray(array *unstable.Node, v reflect.Value) error { switch v.Kind() { - case reflect.Slice: - if v.IsNil() { - v.Set(reflect.MakeSlice(v.Type(), 0, 16)) + case reflect.Map: + // Native fast path for the most common generic target: walk the + // remaining dotted-key path with plain Go map operations and decode + // the value directly, skipping the reflect.Value round-trips + // (stringMapKey, MapIndex, New, SetMapIndex) entirely. + if !d.unmarshalerInterface && v.Type() == mapStringInterfaceType { + return d.descendStrMap(v, path, idx, value) + } + var name string + var key reflect.Value + var err error + fastKey := v.Type().Key() == stringType + if fastKey { + name = d.partString(&part) + key = d.stringMapKey(name) } else { - v.SetLen(0) - } - case reflect.Array: - // arrays are always initialized - case reflect.Interface: - elem := v.Elem() - if !elem.IsValid() { - elem = reflect.New(sliceInterfaceType).Elem() - elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16)) - } else if elem.Kind() == reflect.Slice { - if elem.Type() != sliceInterfaceType { - elem = reflect.New(sliceInterfaceType).Elem() - elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16)) - } else if !elem.CanSet() { - nelem := reflect.New(sliceInterfaceType).Elem() - nelem.Set(reflect.MakeSlice(sliceInterfaceType, elem.Len(), elem.Cap())) - reflect.Copy(nelem, elem) - elem = nelem + key, err = makeMapKey(v.Type().Key(), d.partString(&part)) + if err != nil { + return reflect.Value{}, err } } - err := d.unmarshalArray(array, elem) + if v.IsNil() { + v = reflect.MakeMap(v.Type()) + } + elemType := v.Type().Elem() + existing := v.MapIndex(key) + var elem reflect.Value + switch { + case existing.IsValid(): + elem = reflect.New(elemType).Elem() + elem.Set(existing) + case idx+1 == len(path) && elemType.Kind() == reflect.Interface: + // Fast path: a fresh interface element does not need to be + // materialized, the assigned value is stored directly. + elem = reflect.Zero(elemType) + default: + elem = reflect.New(elemType).Elem() + } + nv, err := d.descend(elem, path, idx+1, expr, value) if err != nil { - return err + return reflect.Value{}, err } - v.Set(elem) - return nil - default: - // TODO: use newDecodeError, but first the parser needs to fill - // array.Data. - return d.typeMismatchError("array", v.Type()) - } - - elemType := v.Type().Elem() - - it := array.Children() - idx := 0 - for it.Next() { - n := it.Node() - - // TODO: optimize - if v.Kind() == reflect.Slice { - elem := reflect.New(elemType).Elem() - - err := d.handleValue(n, elem) - if err != nil { - return err + if nv.IsValid() { + if fastKey { + // The recursion may have overwritten the key buffer. + key = d.stringMapKey(name) } - - v.Set(reflect.Append(v, elem)) - } else { // array - if idx >= v.Len() { - return nil + v.SetMapIndex(key, nv) + } + return v, nil + case reflect.Struct: + plan := planForType(v.Type()) + f, found := plan.lookupBytes(part.bytes()) + if !found { + if part.node != nil { + d.strict.MissingField(expr) } - elem := v.Index(idx) - err := d.handleValue(n, elem) - if err != nil { - return err + return v, nil + } + fv, err := fieldByIndexAlloc(v, f.index) + if err != nil { + return reflect.Value{}, err + } + var nv reflect.Value + if idx+1 == len(path) { + // Leaf field: assign directly. descend's first action for a + // fully-consumed path is exactly this call, so skipping the extra + // frame is equivalent and avoids a call per scalar field. + nv, err = d.assignValue(fv, expr, value) + } else { + nv, err = d.descend(fv, path, idx+1, expr, value) + } + if err != nil { + var mm *typeMismatchError + if errors.As(err, &mm) { + err = &unstable.ParserError{ + Highlight: mm.highlight, + Message: fmt.Sprintf("cannot decode TOML %s into struct field %s.%s of type %s", + mm.toml, v.Type(), f.fieldName, mm.target), + } } - idx++ + return reflect.Value{}, err } - } - - return nil -} - -func (d *decoder) unmarshalInlineTable(itable *unstable.Node, v reflect.Value) error { - // Make sure v is an initialized object. - switch v.Kind() { - case reflect.Map: - if v.IsNil() { - v.Set(reflect.MakeMap(v.Type())) + if nv.IsValid() && fv.CanSet() { + fv.Set(nv) } - case reflect.Struct: - // structs are always initialized. + return v, nil case reflect.Interface: - elem := v.Elem() - if !elem.IsValid() { - elem = makeMapStringInterface() - v.Set(elem) + elem := elemOrNewMap(v) + nv, err := d.descend(elem, path, idx, expr, value) + if err != nil || !nv.IsValid() { + return reflect.Value{}, err } - return d.unmarshalInlineTable(itable, elem) - default: - return unstable.NewParserError(d.p.Raw(itable.Raw), "cannot store inline table in Go type %s", v.Kind()) - } - - it := itable.Children() - for it.Next() { - n := it.Node() - - x, err := d.handleKeyValue(n, v) + return nv, nil + case reflect.Slice: + if v.Len() == 0 { + if v.IsNil() { + v = reflect.MakeSlice(v.Type(), 0, 4) + } + v = reflect.Append(v, reflect.New(v.Type().Elem()).Elem()) + } + elem := v.Index(v.Len() - 1) + nv, err := d.descend(elem, path, idx, expr, value) if err != nil { - return err + return reflect.Value{}, err + } + if nv.IsValid() { + elem.Set(nv) + } + return v, nil + case reflect.Array: + names := make([]string, idx) + for i := range names { + names[i] = path[i].str() + } + cnt := d.arrayCount(d.joinPath(names)) + if cnt == 0 { + cnt = 1 } - if x.IsValid() { - v = x + elemIdx := cnt - 1 + if elemIdx >= v.Len() { + return reflect.Value{}, unstable.NewParserError(keyHighlight(d.p.Data(), part.node), + "cannot reach element %d of array of size %d", elemIdx, v.Len()) } + elem := v.Index(elemIdx) + nv, err := d.descend(elem, path, idx, expr, value) + if err != nil { + return reflect.Value{}, err + } + if nv.IsValid() { + elem.Set(nv) + } + return v, nil + default: + return reflect.Value{}, d.typeMismatchError("table", v.Type(), keyHighlight(d.p.Data(), part.node)) } - - return nil } -func (d *decoder) unmarshalDateTime(value *unstable.Node, v reflect.Value) error { - dt, err := parseDateTime(value.Data) - if err != nil { - return err +// descendStrMap assigns into a native map[string]interface{} target, following +// the remaining dotted-key parts with plain Go map operations and decoding the +// value with decodeAny. It returns the map to store back at this level: a new +// map when v was nil, otherwise v unchanged, since maps are reference types and +// are mutated in place. +func (d *decoder) descendStrMap(v reflect.Value, path []pathPart, idx int, value *unstable.Node) (reflect.Value, error) { + var m map[string]interface{} + if v.IsNil() { + m = make(map[string]interface{}) + v = reflect.ValueOf(m) + } else { + m = v.Interface().(map[string]interface{}) } - v.Set(reflect.ValueOf(dt)) - return nil -} + // Walk intermediate parts, creating or reusing nested generic maps. A + // non-map value at an intermediate key can only occur in a document the + // seen-tracker has already rejected; replacing it mirrors the reflect + // path (elemOrNewMap). + for ; idx < len(path)-1; idx++ { + name := d.partString(&path[idx]) + child, _ := m[name].(map[string]interface{}) + if child == nil { + child = make(map[string]interface{}) + m[name] = child + } + m = child + } -func (d *decoder) unmarshalLocalDate(value *unstable.Node, v reflect.Value) error { - ld, err := parseLocalDate(value.Data) + av, err := d.decodeAny(value) if err != nil { - return err + return reflect.Value{}, err } + m[d.partString(&path[idx])] = av + return v, nil +} - if v.Type() == timeType { - cast := ld.AsTime(time.Local) - v.Set(reflect.ValueOf(cast)) - return nil +// keyHighlight returns a highlight for the given key part node, falling back +// to the start of the document. +func keyHighlight(doc []byte, node *unstable.Node) []byte { + if node == nil { + return doc[0:0] } - - v.Set(reflect.ValueOf(ld)) - - return nil + return doc[node.Raw.Offset : node.Raw.Offset+node.Raw.Length] } -func (d *decoder) unmarshalLocalTime(value *unstable.Node, v reflect.Value) error { - lt, rest, err := parseLocalTime(value.Data) - if err != nil { - return err +// rawValue returns the raw bytes of the value of a key-value expression. +func (d *decoder) rawValue(expr *unstable.Node, value *unstable.Node) []byte { + if value.Kind != unstable.InlineTable && value.Kind != unstable.Array { + return d.p.Raw(value.Raw) } - - if len(rest) > 0 { - return unstable.NewParserError(rest, "extra characters at the end of a local time") + if expr == nil || expr.Kind != unstable.KeyValue { + // Inline container nested in another container: best effort. + return d.p.Raw(value.Raw) } - - v.Set(reflect.ValueOf(lt)) - return nil + // Reconstruct the span of the value: it starts after the equal sign + // following the last part of the key, and stops at the end of the + // expression. + var last unstable.Range + it := expr.Key() + for it.Next() { + last = it.Node().Raw + } + doc := d.p.Data() + i := int(last.Offset + last.Length) + for i < len(doc) && (doc[i] == ' ' || doc[i] == '\t') { + i++ + } + i++ // equal sign + for i < len(doc) && (doc[i] == ' ' || doc[i] == '\t') { + i++ + } + end := int(expr.Raw.Offset + expr.Raw.Length) + return doc[i:end] } -func (d *decoder) unmarshalLocalDateTime(value *unstable.Node, v reflect.Value) error { - ldt, rest, err := parseLocalDateTime(value.Data) - if err != nil { - return err +// unmarshalerOf returns the unstable.Unmarshaler implementation of v, if +// any. It allocates intermediate pointers as needed. +func unmarshalerOf(v reflect.Value) (unstable.Unmarshaler, bool) { + t := v.Type() + if t.Implements(unmarshalerType) { + if v.Kind() == reflect.Ptr && v.IsNil() { + v.Set(reflect.New(t.Elem())) + } + return v.Interface().(unstable.Unmarshaler), true } - - if len(rest) > 0 { - return unstable.NewParserError(rest, "extra characters at the end of a local date time") + if v.CanAddr() && reflect.PtrTo(t).Implements(unmarshalerType) { + return v.Addr().Interface().(unstable.Unmarshaler), true } + return nil, false +} - if v.Type() == timeType { - cast := ldt.AsTime(time.Local) +var unmarshalerType = reflect.TypeOf(new(unstable.Unmarshaler)).Elem() - v.Set(reflect.ValueOf(cast)) - return nil +// assignValue stores the TOML value carried by the node into v. +func (d *decoder) assignValue(v reflect.Value, expr *unstable.Node, value *unstable.Node) (reflect.Value, error) { + if v.Kind() == reflect.Ptr { + if d.unmarshalerInterface { + if u, ok := unmarshalerOf(v); ok { + return v, u.UnmarshalTOML(d.rawValue(expr, value)) + } + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + nv, err := d.assignValue(v.Elem(), expr, value) + if err != nil || !nv.IsValid() { + return reflect.Value{}, err + } + v.Elem().Set(nv) + return v, nil } - v.Set(reflect.ValueOf(ldt)) - - return nil -} - -func (d *decoder) unmarshalBool(value *unstable.Node, v reflect.Value) error { - b := value.Data[0] == 't' + if d.unmarshalerInterface { + if u, ok := unmarshalerOf(v); ok { + return v, u.UnmarshalTOML(d.rawValue(expr, value)) + } + } - switch v.Kind() { - case reflect.Bool: - v.SetBool(b) - case reflect.Interface: - v.Set(reflect.ValueOf(b)) + switch value.Kind { + case unstable.String: + return d.assignString(v, value) + case unstable.Integer: + return d.assignInteger(v, value) + case unstable.Float: + return d.assignFloat(v, value) + case unstable.Bool: + return d.assignBool(v, value) + case unstable.DateTime: + return d.assignDateTime(v, value) + case unstable.LocalDateTime: + return d.assignLocalDateTime(v, value) + case unstable.LocalDate: + return d.assignLocalDate(v, value) + case unstable.LocalTime: + return d.assignLocalTime(v, value) + case unstable.Array: + return d.assignArray(v, expr, value) + case unstable.InlineTable: + return d.assignInlineTable(v, expr, value) default: - return unstable.NewParserError(value.Data, "cannot assign boolean to a %t", b) + return reflect.Value{}, unstable.NewParserError(value.Data, "unsupported value kind %s", value.Kind) } - - return nil } -func (d *decoder) unmarshalFloat(value *unstable.Node, v reflect.Value) error { - f, err := parseFloat(value.Data) - if err != nil { - return err - } - +func (d *decoder) assignString(v reflect.Value, value *unstable.Node) (reflect.Value, error) { switch v.Kind() { - case reflect.Float64: - v.SetFloat(f) - case reflect.Float32: - if f > math.MaxFloat32 { - return unstable.NewParserError(value.Data, "number %f does not fit in a float32", f) - } - v.SetFloat(f) + case reflect.String: + v.SetString(string(value.Data)) + return v, nil case reflect.Interface: - v.Set(reflect.ValueOf(f)) + return boxInto(v, reflect.ValueOf(string(value.Data))) default: - return unstable.NewParserError(value.Data, "float cannot be assigned to %s", v.Kind()) } - - return nil -} - -const ( - maxInt = int64(^uint(0) >> 1) - minInt = -maxInt - 1 -) - -// Maximum value of uint for decoding. Currently the decoder parses the integer -// into an int64. As a result, on architectures where uint is 64 bits, the -// effective maximum uint we can decode is the maximum of int64. On -// architectures where uint is 32 bits, the maximum value we can decode is -// lower: the maximum of uint32. I didn't find a way to figure out this value at -// compile time, so it is computed during initialization. -var maxUint int64 = math.MaxInt64 - -func init() { - m := uint64(^uint(0)) - if m < uint64(maxUint) { - maxUint = int64(m) + if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) { + err := v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(value.Data) + if err != nil { + return reflect.Value{}, unstable.NewParserError(d.p.Raw(value.Raw), "%s", err) + } + return v, nil } + return reflect.Value{}, d.typeMismatchError("string", v.Type(), d.p.Raw(value.Raw)) } -func (d *decoder) unmarshalInteger(value *unstable.Node, v reflect.Value) error { - kind := v.Kind() - if kind == reflect.Float32 || kind == reflect.Float64 { - return d.unmarshalFloat(value, v) +func (d *decoder) assignInteger(v reflect.Value, value *unstable.Node) (reflect.Value, error) { + // Integer values targeting a float field are parsed as floats: they can + // represent (approximately) numbers beyond the int64 range. + if k := v.Kind(); k == reflect.Float32 || k == reflect.Float64 { + return d.assignFloat(v, value) } i, err := parseInteger(value.Data) if err != nil { - return err + return reflect.Value{}, err } - var r reflect.Value - - switch kind { - case reflect.Int64: - v.SetInt(i) - return nil - case reflect.Int32: - if i < math.MinInt32 || i > math.MaxInt32 { - return fmt.Errorf("toml: number %d does not fit in an int32", i) - } - - r = reflect.ValueOf(int32(i)) - case reflect.Int16: - if i < math.MinInt16 || i > math.MaxInt16 { - return fmt.Errorf("toml: number %d does not fit in an int16", i) - } - - r = reflect.ValueOf(int16(i)) - case reflect.Int8: - if i < math.MinInt8 || i > math.MaxInt8 { - return fmt.Errorf("toml: number %d does not fit in an int8", i) - } - - r = reflect.ValueOf(int8(i)) - case reflect.Int: - if i < minInt || i > maxInt { - return fmt.Errorf("toml: number %d does not fit in an int", i) + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if v.OverflowInt(i) { + return reflect.Value{}, unstable.NewParserError(value.Data, "integer value %d cannot be stored in %s", i, v.Type()) } - - r = reflect.ValueOf(int(i)) - case reflect.Uint64: + v.SetInt(i) + return v, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: if i < 0 { - return fmt.Errorf("toml: negative number %d does not fit in an uint64", i) + return reflect.Value{}, unstable.NewParserError(value.Data, "negative integer value %d cannot be stored in %s", i, v.Type()) } - - r = reflect.ValueOf(uint64(i)) - case reflect.Uint32: - if i < 0 || i > math.MaxUint32 { - return fmt.Errorf("toml: negative number %d does not fit in an uint32", i) + if v.OverflowUint(uint64(i)) { + return reflect.Value{}, unstable.NewParserError(value.Data, "integer value %d cannot be stored in %s", i, v.Type()) } + v.SetUint(uint64(i)) + return v, nil + case reflect.Interface: + return boxInto(v, reflect.ValueOf(i)) + default: + } + if ok, err := tryTextUnmarshaler(v, value.Data); ok { + return v, err + } + return reflect.Value{}, d.typeMismatchError("integer", v.Type(), d.p.Raw(value.Raw)) +} - r = reflect.ValueOf(uint32(i)) - case reflect.Uint16: - if i < 0 || i > math.MaxUint16 { - return fmt.Errorf("toml: negative number %d does not fit in an uint16", i) - } +// tryTextUnmarshaler attempts to deliver the raw text of a value to a target +// implementing encoding.TextUnmarshaler. +func tryTextUnmarshaler(v reflect.Value, text []byte) (bool, error) { + if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) { + return true, v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(text) + } + return false, nil +} - r = reflect.ValueOf(uint16(i)) - case reflect.Uint8: - if i < 0 || i > math.MaxUint8 { - return fmt.Errorf("toml: negative number %d does not fit in an uint8", i) - } +func (d *decoder) assignFloat(v reflect.Value, value *unstable.Node) (reflect.Value, error) { + f, err := parseFloat(value.Data) + if err != nil { + return reflect.Value{}, err + } - r = reflect.ValueOf(uint8(i)) - case reflect.Uint: - if i < 0 || i > maxUint { - return fmt.Errorf("toml: negative number %d does not fit in an uint", i) + switch v.Kind() { + case reflect.Float64: + v.SetFloat(f) + return v, nil + case reflect.Float32: + if !math.IsInf(f, 0) && math.Abs(f) > math.MaxFloat32 { + return reflect.Value{}, unstable.NewParserError(value.Data, "float value %f cannot be stored in float32", f) } - - r = reflect.ValueOf(uint(i)) + v.SetFloat(f) + return v, nil case reflect.Interface: - r = reflect.ValueOf(i) + return boxInto(v, reflect.ValueOf(f)) default: - return unstable.NewParserError(d.p.Raw(value.Raw), d.typeMismatchString("integer", v.Type())) } - - if !r.Type().AssignableTo(v.Type()) { - r = r.Convert(v.Type()) + if ok, err := tryTextUnmarshaler(v, value.Data); ok { + return v, err } - - v.Set(r) - - return nil + return reflect.Value{}, d.typeMismatchError("float", v.Type(), d.p.Raw(value.Raw)) } -func (d *decoder) unmarshalString(value *unstable.Node, v reflect.Value) error { +func (d *decoder) assignBool(v reflect.Value, value *unstable.Node) (reflect.Value, error) { + b := value.Data[0] == 't' + switch v.Kind() { - case reflect.String: - v.SetString(string(value.Data)) + case reflect.Bool: + v.SetBool(b) + return v, nil case reflect.Interface: - v.Set(reflect.ValueOf(string(value.Data))) + return boxInto(v, reflect.ValueOf(b)) default: - return unstable.NewParserError(d.p.Raw(value.Raw), d.typeMismatchString("string", v.Type())) } - - return nil + if ok, err := tryTextUnmarshaler(v, value.Data); ok { + return v, err + } + return reflect.Value{}, d.typeMismatchError("boolean", v.Type(), d.p.Raw(value.Raw)) } -func (d *decoder) handleKeyValue(expr *unstable.Node, v reflect.Value) (reflect.Value, error) { - d.strict.EnterKeyValue(expr) +func (d *decoder) assignDateTime(v reflect.Value, value *unstable.Node) (reflect.Value, error) { + t, err := parseDateTime(value.Data) + if err != nil { + return reflect.Value{}, err + } - v, err := d.handleKeyValueInner(expr.Key(), expr.Value(), v) - if d.skipUntilTable { - d.strict.MissingField(expr) - d.skipUntilTable = false + if v.Type() == timeType { + setConcrete(v, t) + return v, nil + } + if v.Kind() == reflect.Interface { + return boxInto(v, reflect.ValueOf(t)) } + return reflect.Value{}, d.typeMismatchError("datetime", v.Type(), d.p.Raw(value.Raw)) +} + +// setConcrete assigns x to v, which must be a settable value of x's concrete +// type. Writing through the address avoids the heap allocation that +// v.Set(reflect.ValueOf(x)) incurs from boxing x into an interface. Settable +// implies addressable: the decoder assigns to struct fields, slice elements, +// and reflect.New-allocated temporaries (map elements are decoded into such +// temporaries), all of which are addressable. +func setConcrete[T any](v reflect.Value, x T) { + *(v.Addr().Interface().(*T)) = x +} - d.strict.ExitKeyValue(expr) +func (d *decoder) assignLocalDateTime(v reflect.Value, value *unstable.Node) (reflect.Value, error) { + dt, rest, err := parseLocalDateTime(value.Data) + if err != nil { + return reflect.Value{}, err + } + if len(rest) > 0 { + return reflect.Value{}, unstable.NewParserError(rest, "extra characters at the end of a local date time") + } - return v, err + switch v.Type() { + case localDateTimeType: + setConcrete(v, dt) + return v, nil + case timeType: + setConcrete(v, dt.AsTime(time.Local)) + return v, nil + } + if v.Kind() == reflect.Interface { + return boxInto(v, reflect.ValueOf(dt)) + } + return reflect.Value{}, d.typeMismatchError("local datetime", v.Type(), d.p.Raw(value.Raw)) } -func (d *decoder) handleKeyValueInner(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) { - if key.Next() { - // Still scoping the key - return d.handleKeyValuePart(key, value, v) +func (d *decoder) assignLocalDate(v reflect.Value, value *unstable.Node) (reflect.Value, error) { + date, err := parseLocalDate(value.Data) + if err != nil { + return reflect.Value{}, err + } + + switch v.Type() { + case localDateType: + setConcrete(v, date) + return v, nil + case timeType: + setConcrete(v, date.AsTime(time.Local)) + return v, nil } - // Done scoping the key. - // v is whatever Go value we need to fill. - return reflect.Value{}, d.handleValue(value, v) + if v.Kind() == reflect.Interface { + return boxInto(v, reflect.ValueOf(date)) + } + return reflect.Value{}, d.typeMismatchError("local date", v.Type(), d.p.Raw(value.Raw)) } -func (d *decoder) keyFromData(keyType reflect.Type, data []byte) (reflect.Value, error) { - switch { - case stringType.AssignableTo(keyType): - return reflect.ValueOf(string(data)), nil +func (d *decoder) assignLocalTime(v reflect.Value, value *unstable.Node) (reflect.Value, error) { + t, rest, err := parseLocalTime(value.Data) + if err != nil { + return reflect.Value{}, err + } + if len(rest) > 0 { + return reflect.Value{}, unstable.NewParserError(rest, "extra characters at the end of a local time") + } - case stringType.ConvertibleTo(keyType): - return reflect.ValueOf(string(data)).Convert(keyType), nil + switch v.Type() { + case localTimeType: + setConcrete(v, t) + return v, nil + case timeType: + setConcrete(v, time.Date(0, 1, 1, t.Hour, t.Minute, t.Second, t.Nanosecond, time.Local)) + return v, nil + } + if v.Kind() == reflect.Interface { + return boxInto(v, reflect.ValueOf(t)) + } + return reflect.Value{}, d.typeMismatchError("local time", v.Type(), d.p.Raw(value.Raw)) +} - case keyType.Implements(textUnmarshalerType): - mk := reflect.New(keyType.Elem()) - if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { - return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err) +func (d *decoder) assignArray(v reflect.Value, expr *unstable.Node, value *unstable.Node) (reflect.Value, error) { + // Count the elements to allocate the target in one go. + count := 0 + cit := value.Children() + for cit.Next() { + if cit.Node().Kind != unstable.Comment { + count++ } - return mk, nil + } - case reflect.PointerTo(keyType).Implements(textUnmarshalerType): - mk := reflect.New(keyType) - if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { - return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err) + switch v.Kind() { + case reflect.Slice: + // Allocate the backing array once at its final length and assign each + // element in place. This avoids a reflect.New allocation per element + // and the repeated growth checks of reflect.Append. + slice := reflect.MakeSlice(v.Type(), count, count) + i := 0 + it := value.Children() + for it.Next() { + n := it.Node() + if n.Kind == unstable.Comment { + continue + } + elem := slice.Index(i) + nv, err := d.assignValue(elem, nil, n) + if err != nil { + return reflect.Value{}, err + } + if nv.IsValid() { + elem.Set(nv) + } + i++ } - return mk.Elem(), nil - - case keyType.Kind() == reflect.Int || keyType.Kind() == reflect.Int8 || keyType.Kind() == reflect.Int16 || keyType.Kind() == reflect.Int32 || keyType.Kind() == reflect.Int64: - key, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from integer: %w", stringType, err) + return slice, nil + case reflect.Array: + it := value.Children() + i := 0 + for it.Next() { + n := it.Node() + if n.Kind == unstable.Comment { + continue + } + if i >= v.Len() { + // Extra elements are dropped when the target array is too + // small. + break + } + elem := v.Index(i) + nv, err := d.assignValue(elem, nil, n) + if err != nil { + return reflect.Value{}, err + } + elem.Set(nv) + i++ } - return reflect.ValueOf(key).Convert(keyType), nil - case keyType.Kind() == reflect.Uint || keyType.Kind() == reflect.Uint8 || keyType.Kind() == reflect.Uint16 || keyType.Kind() == reflect.Uint32 || keyType.Kind() == reflect.Uint64: - key, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from unsigned integer: %w", stringType, err) + return v, nil + case reflect.Interface: + // Build the []interface{} natively: each element is decoded straight + // into a Go value with no intermediate addressable reflect.Value and + // no reflect round-trip, and nested arrays recurse the same way. + slice := make([]interface{}, 0, count) + it := value.Children() + for it.Next() { + n := it.Node() + if n.Kind == unstable.Comment { + continue + } + ev, err := d.decodeAny(n) + if err != nil { + return reflect.Value{}, err + } + slice = append(slice, ev) } - return reflect.ValueOf(key).Convert(keyType), nil + return boxInto(v, reflect.ValueOf(slice)) + default: + } + return reflect.Value{}, d.typeMismatchError("array", v.Type(), d.rawValue(expr, value)) +} - case keyType.Kind() == reflect.Float32: - key, err := strconv.ParseFloat(string(data), 32) +// decodeAny decodes a value node into a native Go value (the representation +// used for interface{} targets), without going through reflect. Scalars and +// arrays are handled directly; inline tables still defer to the reflect-based +// path so that their dotted-key merge semantics remain identical. +func (d *decoder) decodeAny(n *unstable.Node) (interface{}, error) { + switch n.Kind { + case unstable.String: + return string(n.Data), nil + case unstable.Integer: + i, err := parseInteger(n.Data) + return i, err + case unstable.Float: + f, err := parseFloat(n.Data) + return f, err + case unstable.Bool: + return n.Data[0] == 't', nil + case unstable.Array: + count := 0 + cit := n.Children() + for cit.Next() { + if cit.Node().Kind != unstable.Comment { + count++ + } + } + slice := make([]interface{}, 0, count) + it := n.Children() + for it.Next() { + c := it.Node() + if c.Kind == unstable.Comment { + continue + } + ev, err := d.decodeAny(c) + if err != nil { + return nil, err + } + slice = append(slice, ev) + } + return slice, nil + case unstable.InlineTable: + // Build the map natively: navigate each (possibly dotted) key with + // plain Go map operations and decode each value with decodeAny. The + // seen-tracker has already rejected duplicate or conflicting keys, so + // intermediate parts can be created/merged without revalidation. + count := 0 + cit := n.Children() + for cit.Next() { + count++ + } + m := make(map[string]interface{}, count) + it := n.Children() + for it.Next() { + kv := it.Node() + if err := d.setAnyKey(m, kv.Key(), kv.Value()); err != nil { + return nil, err + } + } + return m, nil + case unstable.DateTime: + t, err := parseDateTime(n.Data) + return t, err + case unstable.LocalDateTime: + dt, rest, err := parseLocalDateTime(n.Data) if err != nil { - return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from float: %w", stringType, err) + return nil, err } - return reflect.ValueOf(float32(key)), nil - - case keyType.Kind() == reflect.Float64: - key, err := strconv.ParseFloat(string(data), 64) + if len(rest) > 0 { + return nil, unstable.NewParserError(rest, "extra characters at the end of a local date time") + } + return dt, nil + case unstable.LocalDate: + date, err := parseLocalDate(n.Data) + return date, err + case unstable.LocalTime: + t, rest, err := parseLocalTime(n.Data) if err != nil { - return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from float: %w", stringType, err) + return nil, err } - return reflect.ValueOf(float64(key)), nil + if len(rest) > 0 { + return nil, unstable.NewParserError(rest, "extra characters at the end of a local time") + } + return t, nil + default: + return nil, unstable.NewParserError(n.Data, "unsupported value kind %s", n.Kind) } - return reflect.Value{}, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", stringType, keyType) } -func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) { - // contains the replacement for v - var rv reflect.Value - - // First, dispatch over v to make sure it is a valid object. - // There is no guarantee over what it could be. +func (d *decoder) assignInlineTable(v reflect.Value, expr *unstable.Node, value *unstable.Node) (reflect.Value, error) { switch v.Kind() { case reflect.Map: - vt := v.Type() - - mk, err := d.keyFromData(vt.Key(), key.Node().Data) + // Inline tables are self-contained: they fully replace the target. + v = reflect.MakeMap(v.Type()) + case reflect.Struct: + // fields are set in place + case reflect.Interface: + elem := reflect.ValueOf(map[string]interface{}{}) + nv, err := d.assignInlineTable(elem, expr, value) if err != nil { return reflect.Value{}, err } + return boxInto(v, nv) + default: + return reflect.Value{}, d.typeMismatchError("inline table", v.Type(), d.rawValue(expr, value)) + } - // If the map does not exist, create it. - if v.IsNil() { - v = reflect.MakeMap(vt) - rv = v - } - - mv := v.MapIndex(mk) - set := false - if !mv.IsValid() || key.IsLast() { - set = true - mv = reflect.New(v.Type().Elem()).Elem() - } - - nv, err := d.handleKeyValueInner(key, value, mv) + it := value.Children() + for it.Next() { + kv := it.Node() + // Build the path from the key of this key-value. Keys of inline + // tables rarely have more than a few parts. + var pathBuf [4]pathPart + path := pathBuf[:0] + kit := kv.Key() + for kit.Next() { + path = append(path, pathPart{node: kit.Node()}) + } + nv, err := d.descend(v, path, 0, kv, kv.Value()) if err != nil { return reflect.Value{}, err } if nv.IsValid() { - mv = nv - set = true - } - - if set { - v.SetMapIndex(mk, mv) + v = nv } - case reflect.Struct: - path, found := structFieldPath(v, string(key.Node().Data)) - if !found { - d.skipUntilTable = true - break - } - - if d.errorContext == nil { - d.errorContext = new(errorContext) - } - t := v.Type() - d.errorContext.Struct = t - d.errorContext.Field = path + } + return v, nil +} - f := fieldByIndex(v, path) +// boxInto returns the value to store in place of the interface value v. The +// caller stores the result in the slot v was found in, which performs the +// interface conversion, so the concrete value can be returned as-is. +func boxInto(v reflect.Value, c reflect.Value) (reflect.Value, error) { + if !c.Type().AssignableTo(v.Type()) { + return reflect.Value{}, fmt.Errorf("toml: cannot store %s into %s", c.Type(), v.Type()) + } + return c, nil +} - if !f.CanAddr() { - // If the field is not addressable, need to take a slower path and - // make a copy of the struct itself to a new location. - nvp := reflect.New(v.Type()) - nvp.Elem().Set(v) - v = nvp.Elem() - _, err := d.handleKeyValuePart(key, value, v) - if err != nil { - return reflect.Value{}, err - } - return nvp.Elem(), nil - } - x, err := d.handleKeyValueInner(key, value, f) - if err != nil { - return reflect.Value{}, err - } +var ( + interfaceType = reflect.TypeOf(new(interface{})).Elem() + localDateType = reflect.TypeOf(LocalDate{}) + localTimeType = reflect.TypeOf(LocalTime{}) + localDateTimeType = reflect.TypeOf(LocalDateTime{}) +) - if x.IsValid() { - f.Set(x) - } - d.errorContext.Struct = nil - d.errorContext.Field = nil - case reflect.Interface: - v = v.Elem() +// structPlan caches the mapping between TOML keys and the fields of a struct +// type. byFold, keyed by the lowercased name, resolves any key on its own when +// no two fields fold to the same name (the overwhelmingly common case, marked +// by hasCollision == false): TOML keys are usually lowercase and never match +// the exact (capitalized) Go field names, so the byName probe was always a +// wasted lookup. byName (the exact names) is only consulted, first, when +// fields do collide under folding, to preserve the exact-match-wins tiebreak. +type structPlan struct { + byName map[string]structField + byFold map[string]structField + hasCollision bool +} - // Following encoding/json: decoding an object into an - // interface{}, it needs to always hold a - // map[string]interface{}. This is for the types to be - // consistent whether a previous value was set or not. - if !v.IsValid() || v.Type() != mapStringInterfaceType { - v = makeMapStringInterface() - } +type structField struct { + index []int + fieldName string +} - x, err := d.handleKeyValuePart(key, value, v) - if err != nil { - return reflect.Value{}, err - } - if x.IsValid() { - v = x - } - rv = v - case reflect.Ptr: - elem := v.Elem() - if !elem.IsValid() { - ptr := reflect.New(v.Type().Elem()) - v.Set(ptr) - rv = v - elem = ptr.Elem() +// foldBufSize bounds the stack buffer used to lowercase keys without +// allocating. Keys longer than this (extremely rare) take the strings.ToLower +// fallback. +const foldBufSize = 68 + +// lookup and lookupBytes keep the hot path to a single inlinable byFold lookup. +// byFold is indexed by both the exact field/tag names and their lowercased +// forms, so that lookup resolves the two common cases — a lowercase key, or a +// key matching the field's own casing — directly. byName is consulted first +// only for types whose fields collide under case-folding, to preserve the +// exact-match-wins tiebreak. The buffer-fold for other casings lives +// out-of-line so it does not bloat the hot path. +func (p *structPlan) lookup(name string) (structField, bool) { + if p.hasCollision { + if f, ok := p.byName[name]; ok { + return f, true } + } + if f, ok := p.byFold[name]; ok { + return f, true + } + return p.lookupFoldStr(name) +} - elem2, err := d.handleKeyValuePart(key, value, elem) - if err != nil { - return reflect.Value{}, err +func (p *structPlan) lookupBytes(name []byte) (structField, bool) { + if p.hasCollision { + if f, ok := p.byName[string(name)]; ok { // does not allocate + return f, true } - if elem2.IsValid() { - elem = elem2 - } - v.Elem().Set(elem) - default: - return reflect.Value{}, fmt.Errorf("unhandled kv part: %s", v.Kind()) } - - return rv, nil + if f, ok := p.byFold[string(name)]; ok { // does not allocate + return f, true + } + return p.lookupFold(name) } -func initAndDereferencePointer(v reflect.Value) reflect.Value { - var elem reflect.Value - if v.IsNil() { - ptr := reflect.New(v.Type().Elem()) - v.Set(ptr) +// lookupFold resolves keys whose casing matches neither the exact nor the +// lowercased index: it folds to lowercase (in a stack buffer for ASCII, so no +// allocation) and retries; only non-ASCII or oversized keys hit strings.ToLower. +func (p *structPlan) lookupFold(name []byte) (structField, bool) { + if len(name) <= foldBufSize { + // Fold into a stack buffer: len(name) <= cap(buf), so the append + // never reallocates and nothing escapes to the heap. + var buf [foldBufSize]byte + b := buf[:0] + ascii := true + for _, c := range name { + if c >= 0x80 { + ascii = false + break + } + if c >= 'A' && c <= 'Z' { + c += 'a' - 'A' + } + b = append(b, c) + } + if ascii { + f, ok := p.byFold[string(b)] // does not allocate + return f, ok + } } - elem = v.Elem() - return elem + f, ok := p.byFold[strings.ToLower(string(name))] + return f, ok } -// Same as reflect.Value.FieldByIndex, but creates pointers if needed. -func fieldByIndex(v reflect.Value, path []int) reflect.Value { - for _, x := range path { - v = v.Field(x) - - if v.Kind() == reflect.Ptr { - if v.IsNil() { - v.Set(reflect.New(v.Type().Elem())) +func (p *structPlan) lookupFoldStr(name string) (structField, bool) { + if len(name) <= foldBufSize { + // Fold into a stack buffer: len(name) <= cap(buf), so the append + // never reallocates and nothing escapes to the heap. + var buf [foldBufSize]byte + b := buf[:0] + ascii := true + for i := 0; i < len(name); i++ { + c := name[i] + if c >= 0x80 { + ascii = false + break + } + if c >= 'A' && c <= 'Z' { + c += 'a' - 'A' } - v = v.Elem() + b = append(b, c) + } + if ascii { + f, ok := p.byFold[string(b)] // does not allocate + return f, ok } } - return v + f, ok := p.byFold[strings.ToLower(name)] + return f, ok } -type fieldPathsMap = map[string][]int - -var globalFieldPathsCache atomic.Value // map[danger.TypeID]fieldPathsMap +var structPlans sync.Map // reflect.Type -> *structPlan -func structFieldPath(v reflect.Value, name string) ([]int, bool) { - t := v.Type() - - cache, _ := globalFieldPathsCache.Load().(map[danger.TypeID]fieldPathsMap) - fieldPaths, ok := cache[danger.MakeTypeID(t)] - - if !ok { - fieldPaths = map[string][]int{} - - forEachField(t, nil, func(name string, path []int) { - fieldPaths[name] = path - // extra copy for the case-insensitive match - fieldPaths[strings.ToLower(name)] = path - }) - - newCache := make(map[danger.TypeID]fieldPathsMap, len(cache)+1) - newCache[danger.MakeTypeID(t)] = fieldPaths - for k, v := range cache { - newCache[k] = v - } - globalFieldPathsCache.Store(newCache) +func planForType(t reflect.Type) *structPlan { + if plan, ok := structPlans.Load(t); ok { + return plan.(*structPlan) } + plan := buildPlan(t) + structPlans.Store(t, plan) + return plan +} - path, ok := fieldPaths[name] - if !ok { - path, ok = fieldPaths[strings.ToLower(name)] +func buildPlan(t reflect.Type) *structPlan { + plan := &structPlan{ + byName: map[string]structField{}, + byFold: map[string]structField{}, } - return path, ok + addFields(plan, t, nil, map[reflect.Type]bool{}) + return plan } -func forEachField(t reflect.Type, path []int, do func(name string, path []int)) { - n := t.NumField() - for i := 0; i < n; i++ { - f := t.Field(i) +// addFields flattens the fields of t into the plan. visited holds the types +// being flattened on the current branch: a struct type that embeds itself +// (directly or through other types) is not descended into again, mirroring +// buildEncPlan. Without this guard a recursive embedding would recurse +// forever; the repeated fields are unreachable by flattening anyway. +func addFields(plan *structPlan, t reflect.Type, prefix []int, visited map[reflect.Type]bool) { + if visited[t] { + return + } + visited[t] = true + defer delete(visited, t) - if !f.Anonymous && f.PkgPath != "" { - // only consider exported fields. - continue + var embedded []reflect.StructField + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + tag, tagged := f.Tag.Lookup("toml") + name := f.Name + explicitName := "" + if tagged { + // A tag of exactly "-" drops the field. "-," names it "-". + if tag == "-" { + continue + } + parts := strings.SplitN(tag, ",", 2) + explicitName = parts[0] + if explicitName != "" { + name = explicitName + } } - - fieldPath := append(path, i) - fieldPath = fieldPath[:len(fieldPath):len(fieldPath)] - - name := f.Tag.Get("toml") - if name == "-" { + if f.Anonymous { + ft := f.Type + if ft.Kind() == reflect.Ptr { + ft = ft.Elem() + } + if ft.Kind() != reflect.Struct { + // Embedded non-struct fields are not decoded into. + continue + } + if explicitName == "" { + // Embedded structs without an explicit tag name are flattened, + // even when their type is unexported: only their own exported + // fields are reachable. A tag that only sets options (e.g. + // `,inline`) still flattens, matching encoding/json and the + // encoder's behavior. + embedded = append(embedded, f) + continue + } + // An embedded struct given an explicit tag name acts as a regular + // named field. + } else if f.PkgPath != "" { + // unexported continue } - - if i := strings.IndexByte(name, ','); i >= 0 { - name = name[:i] + index := make([]int, 0, len(prefix)+1) + index = append(index, prefix...) + index = append(index, i) + sf := structField{index: index, fieldName: f.Name} + if _, ok := plan.byName[name]; !ok { + plan.byName[name] = sf } - - if f.Anonymous && name == "" { - t2 := f.Type - if t2.Kind() == reflect.Ptr { - t2 = t2.Elem() - } - - if t2.Kind() == reflect.Struct { - forEachField(t2, fieldPath, do) + lower := strings.ToLower(name) + if _, ok := plan.byFold[lower]; !ok { + plan.byFold[lower] = sf + } else { + // Two distinct fields fold to the same name: case-insensitive + // matching is ambiguous, so lookups must consult byName first to + // keep the exact-match-wins tiebreak deterministic. + plan.hasCollision = true + } + // Index the exact (cased) name as well, so a key written with the + // field's own casing resolves in a single byFold lookup. Only fields + // whose name is not already lowercase need this extra entry. Any name + // that would conflict here also collides under folding (handled + // above), so byName-first preserves the exact tiebreak in that case. + if name != lower { + if _, ok := plan.byFold[name]; !ok { + plan.byFold[name] = sf } - continue } + } + // Embedded structs are flattened after the regular fields, so that + // shallower fields win. + for _, f := range embedded { + ft := f.Type + if ft.Kind() == reflect.Ptr { + ft = ft.Elem() + } + index := make([]int, 0, len(prefix)+1) + index = append(index, prefix...) + idx := f.Index[0] + index = append(index, idx) + addFields(plan, ft, index, visited) + } +} - if name == "" { - name = f.Name +// fieldByIndexAlloc returns the field of v at the given index path, +// allocating intermediate embedded pointers as needed. +func fieldByIndexAlloc(v reflect.Value, index []int) (reflect.Value, error) { + // Fast path for non-embedded fields, which have a single-element index: + // no intermediate pointer dereferencing is possible. + if len(index) == 1 { + return v.Field(index[0]), nil + } + for i, x := range index { + if i > 0 { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + if !v.CanSet() { + // A nil embedded pointer of unexported type cannot be + // allocated: reflect forbids setting it. Match + // encoding/json and report it instead of panicking. + return reflect.Value{}, fmt.Errorf("toml: cannot set embedded pointer to unexported struct: %s", v.Type().Elem()) + } + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } } - - do(name, fieldPath) + v = v.Field(x) } + return v, nil } diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go b/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go index f526bf2c..2cf3bdb6 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go @@ -1,10 +1,8 @@ package unstable import ( + "errors" "fmt" - "unsafe" - - "github.com/pelletier/go-toml/v2/internal/danger" ) // Iterator over a sequence of nodes. @@ -23,8 +21,8 @@ type Iterator struct { node *Node } -// Next moves the iterator forward and returns true if points to a -// node, false otherwise. +// Next moves the iterator forward and returns true if points to a node, false +// otherwise. func (c *Iterator) Next() bool { if !c.started { c.started = true @@ -35,7 +33,7 @@ func (c *Iterator) Next() bool { } // IsLast returns true if the current node of the iterator is the last -// one. Subsequent calls to Next() will return false. +// one. Subsequent calls to Next() will return false. func (c *Iterator) IsLast() bool { return c.node.next == 0 } @@ -53,8 +51,8 @@ func (c *Iterator) Node() *Node { // - Array have one child per element in the array. // - InlineTable have one child per key-value in the table (each of kind // InlineTable). -// - KeyValue have at least two children. The first one is the value. The rest -// make a potentially dotted key. +// - KeyValue have at least two children. The first one is the value. The +// rest make a potentially dotted key. // - Table and ArrayTable's children represent a dotted key (same as // KeyValue, but without the first node being the value). // @@ -65,17 +63,11 @@ type Node struct { Raw Range // Raw bytes from the input. Data []byte // Node value (either allocated or referencing the input). - // References to other nodes, as offsets in the backing array - // from this node. References can go backward, so those can be - // negative. - next int // 0 if last element - child int // 0 if no child -} - -// Range of bytes in the document. -type Range struct { - Offset uint32 - Length uint32 + // References to other nodes, as 1-based indexes into the parser's arena. + // 0 means no node. + parser *Parser + next int32 + child int32 } // Next returns a pointer to the next node, or nil if there is no next node. @@ -83,37 +75,33 @@ func (n *Node) Next() *Node { if n.next == 0 { return nil } - ptr := unsafe.Pointer(n) - size := unsafe.Sizeof(Node{}) - return (*Node)(danger.Stride(ptr, size, n.next)) + return &n.parser.nodes[n.next-1] } // Child returns a pointer to the first child node of this node. Other children -// can be accessed calling Next on the first child. Returns an nil if this Node -// has no child. +// can be accessed calling Next on the first child. Returns nil if there is no +// child node. func (n *Node) Child() *Node { if n.child == 0 { return nil } - ptr := unsafe.Pointer(n) - size := unsafe.Sizeof(Node{}) - return (*Node)(danger.Stride(ptr, size, n.child)) + return &n.parser.nodes[n.child-1] } // Valid returns true if the node's kind is set (not to Invalid). func (n *Node) Valid() bool { - return n != nil + return n != nil && n.Kind != Invalid } // Key returns the children nodes making the Key on a supported node. Panics -// otherwise. They are guaranteed to be all be of the Kind Key. A simple key +// otherwise. They are guaranteed to be all be of the Kind Key. A simple key // would return just one element. func (n *Node) Key() Iterator { switch n.Kind { case KeyValue: value := n.Child() if !value.Valid() { - panic(fmt.Errorf("KeyValue should have at least two children")) + panic(errors.New("KeyValue should have at least two children")) } return Iterator{node: value.Next()} case Table, ArrayTable: @@ -124,7 +112,7 @@ func (n *Node) Key() Iterator { } // Value returns a pointer to the value node of a KeyValue. -// Guaranteed to be non-nil. Panics if not called on a KeyValue node, +// Guaranteed to be non-nil. Panics if not called on a KeyValue node, // or if the Children are malformed. func (n *Node) Value() *Node { return n.Child() diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/bridge.go b/vendor/github.com/pelletier/go-toml/v2/unstable/bridge.go new file mode 100644 index 00000000..5482bde0 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/bridge.go @@ -0,0 +1,21 @@ +package unstable + +import "github.com/pelletier/go-toml/v2/internal/parserbridge" + +// Expose the non-AST scanners to the root toml package without committing to +// them in the public API. See internal/parserbridge for the rationale. +// +//nolint:gochecknoinits // load-time wiring of an internal bridge (see internal/parserbridge) +func init() { + parserbridge.ScanScalar = func(p any, b []byte) (kind int, raw, value, rest []byte, err error) { + k, raw, value, rest, err := p.(*Parser).scanScalar(b) + return int(k), raw, value, rest, err + } + parserbridge.ScanKey = func(p any, b []byte, dst [][]byte) (parts [][]byte, raw, rest []byte, err error) { + return p.(*Parser).scanKey(b, dst) + } + parserbridge.ScanComment = scanComment + parserbridge.ParseValue = func(p any, b []byte) (node any, rest []byte, err error) { + return p.(*Parser).parseValue(b) + } +} diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go b/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go deleted file mode 100644 index 9538e30d..00000000 --- a/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go +++ /dev/null @@ -1,71 +0,0 @@ -package unstable - -// root contains a full AST. -// -// It is immutable once constructed with Builder. -type root struct { - nodes []Node -} - -// Iterator over the top level nodes. -func (r *root) Iterator() Iterator { - it := Iterator{} - if len(r.nodes) > 0 { - it.node = &r.nodes[0] - } - return it -} - -func (r *root) at(idx reference) *Node { - return &r.nodes[idx] -} - -type reference int - -const invalidReference reference = -1 - -func (r reference) Valid() bool { - return r != invalidReference -} - -type builder struct { - tree root - lastIdx int -} - -func (b *builder) Tree() *root { - return &b.tree -} - -func (b *builder) NodeAt(ref reference) *Node { - return b.tree.at(ref) -} - -func (b *builder) Reset() { - b.tree.nodes = b.tree.nodes[:0] - b.lastIdx = 0 -} - -func (b *builder) Push(n Node) reference { - b.lastIdx = len(b.tree.nodes) - b.tree.nodes = append(b.tree.nodes, n) - return reference(b.lastIdx) -} - -func (b *builder) PushAndChain(n Node) reference { - newIdx := len(b.tree.nodes) - b.tree.nodes = append(b.tree.nodes, n) - if b.lastIdx >= 0 { - b.tree.nodes[b.lastIdx].next = newIdx - b.lastIdx - } - b.lastIdx = newIdx - return reference(b.lastIdx) -} - -func (b *builder) AttachChild(parent reference, child reference) { - b.tree.nodes[parent].child = int(child) - int(parent) -} - -func (b *builder) Chain(from reference, to reference) { - b.tree.nodes[from].next = int(to) - int(from) -} diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go b/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go index ff9df1be..f1912f77 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go @@ -6,28 +6,40 @@ import "fmt" type Kind int const ( - // Meta + // Invalid represents an invalid meta node. Invalid Kind = iota + // Comment represents a comment meta node. Comment + // Key represents a key meta node. Key - // Top level structures + // Table represents a top-level table. Table + // ArrayTable represents a top-level array table. ArrayTable + // KeyValue represents a top-level key value. KeyValue - // Containers values + // Array represents an array container value. Array + // InlineTable represents an inline table container value. InlineTable - // Values + // String represents a string value. String + // Bool represents a boolean value. Bool + // Float represents a floating point value. Float + // Integer represents an integer value. Integer + // LocalDate represents a local date value. LocalDate + // LocalTime represents a local time value. LocalTime + // LocalDateTime represents a local date/time value. LocalDateTime + // DateTime represents a data/time value. DateTime ) @@ -67,5 +79,5 @@ func (k Kind) String() string { case DateTime: return "DateTime" } - panic(fmt.Errorf("Kind.String() not implemented for '%d'", k)) + panic(fmt.Errorf("Kind.String() not implemented for kind %d", int(k))) } diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/marshaler.go b/vendor/github.com/pelletier/go-toml/v2/unstable/marshaler.go new file mode 100644 index 00000000..40c2e978 --- /dev/null +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/marshaler.go @@ -0,0 +1,29 @@ +package unstable + +// Marshaler is implemented by types that can marshal themselves into a raw +// TOML description. The returned bytes are spliced verbatim into the encoded +// document, so they must be valid TOML for the position they end up in: +// +// - A single value (string, integer, array, inline table, …) is emitted +// inline, as in `key = `. +// - One or more key-value lines (optionally with relative sub-table headers) +// is emitted as the body of a `[key]` table. +// - At the document root, the bytes are emitted as the whole document. +// +// The encoder decides between those forms by parsing the returned bytes, and +// reports an error when they are not valid TOML for their position; see +// Encoder.EnableMarshalerInterface in the root toml package. MarshalTOML can +// be called more than once for the same value during a single encode, so it +// must be deterministic. +// +// Marshaler is the encoding counterpart of Unmarshaler. +type Marshaler interface { + MarshalTOML() ([]byte, error) +} + +// MarshalTOML implements Marshaler. It returns the raw TOML bytes verbatim, +// mirroring json.RawMessage.MarshalJSON. The value receiver means both +// RawMessage and *RawMessage satisfy Marshaler. +func (m RawMessage) MarshalTOML() ([]byte, error) { + return []byte(m), nil +} diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go b/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go index 50358a44..cf72bb74 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go @@ -2,11 +2,10 @@ package unstable import ( "bytes" + "encoding/binary" + "errors" "fmt" - "unicode" - - "github.com/pelletier/go-toml/v2/internal/characters" - "github.com/pelletier/go-toml/v2/internal/danger" + "unicode/utf8" ) // ParserError describes an error relative to the content of the document. @@ -43,23 +42,34 @@ func NewParserError(highlight []byte, format string, args ...interface{}) error // Don't forget to check Error() after you're done parsing. // // Each top-level expression needs to be fully processed before calling -// NextExpression() again. Otherwise, calls to various Node methods may panic if -// the parser has moved on the next expression. +// NextExpression() again. Otherwise, calls to various Node methods may panic +// if the parser has moved on the next expression. // // For performance reasons, go-toml doesn't make a copy of the input bytes to // the parser. Make sure to copy all the bytes you need to outlive the slice // given to the parser. type Parser struct { - data []byte - builder builder - ref reference - left []byte - err error - first bool - KeepComments bool + + data []byte + left []byte + nodes []Node + err error + + // nesting is the current depth of nested arrays and inline tables being + // parsed. It guards parseVal/parseValArray/parseInlineTable against + // unbounded mutual recursion, which would otherwise let a deeply nested + // document overflow the goroutine stack (an unrecoverable fatal error). + nesting int } +// maxValueNesting is the maximum depth of nested arrays and inline tables the +// parser accepts. Beyond it, parsing fails with a ParserError instead of +// risking a stack overflow. Real-world TOML documents nest only a handful of +// levels deep, so this bound is generous; it matches the limit used by the +// standard library's encoding/json. +const maxValueNesting = 10000 + // Data returns the slice provided to the last call to Reset. func (p *Parser) Data() []byte { return p.data @@ -69,9 +79,17 @@ func (p *Parser) Data() []byte { // input. If the argument is not a subslice of the parser input, this function // panics. func (p *Parser) Range(b []byte) Range { + // b is a subslice of p.data if and only if they share the same backing + // array. In that case, because subslicing cannot extend capacity, the + // number of bytes between the start of b and the end of the backing array + // (its capacity) identifies the offset of b within data. + offset := cap(p.data) - cap(b) + if offset < 0 || offset+len(b) > len(p.data) { + panic(errors.New("not a slice of the data slice")) + } return Range{ - Offset: uint32(danger.SubsliceOffset(p.data, b)), - Length: uint32(len(b)), + Offset: uint32(offset), //nolint:gosec // TOML documents are small + Length: uint32(len(b)), //nolint:gosec // TOML documents are small } } @@ -83,58 +101,11 @@ func (p *Parser) Raw(raw Range) []byte { // Reset brings the parser to its initial state for a given input. It wipes an // reuses internal storage to reduce allocation. func (p *Parser) Reset(b []byte) { - p.builder.Reset() - p.ref = invalidReference p.data = b p.left = b + p.nodes = p.nodes[:0] p.err = nil - p.first = true -} - -// NextExpression parses the next top-level expression. If an expression was -// successfully parsed, it returns true. If the parser is at the end of the -// document or an error occurred, it returns false. -// -// Retrieve the parsed expression with Expression(). -func (p *Parser) NextExpression() bool { - if len(p.left) == 0 || p.err != nil { - return false - } - - p.builder.Reset() - p.ref = invalidReference - - for { - if len(p.left) == 0 || p.err != nil { - return false - } - - if !p.first { - p.left, p.err = p.parseNewline(p.left) - } - - if len(p.left) == 0 || p.err != nil { - return false - } - - p.ref, p.left, p.err = p.parseExpression(p.left) - - if p.err != nil { - return false - } - - p.first = false - - if p.ref.Valid() { - return true - } - } -} - -// Expression returns a pointer to the node representing the last successfully -// parsed expression. -func (p *Parser) Expression() *Node { - return p.builder.NodeAt(p.ref) + p.nesting = 0 } // Error returns any error that has occurred during parsing. @@ -142,6 +113,12 @@ func (p *Parser) Error() error { return p.err } +// Range of bytes in the document. +type Range struct { + Offset uint32 + Length uint32 +} + // Position describes a position in the input. type Position struct { // Number of bytes from the beginning of the input. @@ -158,16 +135,23 @@ type Shape struct { End Position } -func (p *Parser) position(b []byte) Position { - offset := danger.SubsliceOffset(p.data, b) - - lead := p.data[:offset] - - return Position{ +func (p *Parser) position(offset int) Position { + pos := Position{ Offset: offset, - Line: bytes.Count(lead, []byte{'\n'}) + 1, - Column: len(lead) - bytes.LastIndex(lead, []byte{'\n'}), + Line: 1, + Column: 1, + } + b := p.data[:offset] + for { + idx := bytes.IndexByte(b, '\n') + if idx < 0 { + break + } + pos.Line++ + b = b[idx+1:] } + pos.Column = len(b) + 1 + return pos } // Shape returns the shape of the given range in the input. Will @@ -175,1071 +159,1528 @@ func (p *Parser) position(b []byte) Position { func (p *Parser) Shape(r Range) Shape { raw := p.Raw(r) return Shape{ - Start: p.position(raw), - End: p.position(raw[r.Length:]), + Start: p.position(int(r.Offset)), + End: p.position(int(r.Offset) + len(raw)), } } -func (p *Parser) parseNewline(b []byte) ([]byte, error) { - if b[0] == '\n' { - return b[1:], nil +// Expression returns a pointer to the node representing the last successfully +// parsed expression. +func (p *Parser) Expression() *Node { + if len(p.nodes) == 0 { + return nil } + return &p.nodes[0] +} - if b[0] == '\r' { - _, rest, err := scanWindowsNewline(b) - return rest, err +// push appends a node to the arena and returns its handle (1-based index). +func (p *Parser) push(n Node) int32 { + if len(p.nodes) == cap(p.nodes) { + // Grow by 2x: large expressions (huge arrays) would otherwise grow + // the arena in small steps, copying it repeatedly. + newCap := 2 * cap(p.nodes) + if newCap < 64 { + newCap = 64 + } + nodes := make([]Node, len(p.nodes), newCap) + copy(nodes, p.nodes) + p.nodes = nodes } - - return nil, NewParserError(b[0:1], "expected newline but got %#U", b[0]) + n.parser = p + p.nodes = append(p.nodes, n) + return int32(len(p.nodes)) //nolint:gosec // node counts are bounded by document size } -func (p *Parser) parseComment(b []byte) (reference, []byte, error) { - ref := invalidReference - data, rest, err := scanComment(b) - if p.KeepComments && err == nil { - ref = p.builder.Push(Node{ - Kind: Comment, - Raw: p.Range(data), - Data: data, - }) - } - return ref, rest, err +// at returns a pointer to the node with the given handle. Only valid until +// the next call to push. +func (p *Parser) at(handle int32) *Node { + return &p.nodes[handle-1] } -func (p *Parser) parseExpression(b []byte) (reference, []byte, error) { - // expression = ws [ comment ] - // expression =/ ws keyval ws [ comment ] - // expression =/ ws table ws [ comment ] - ref := invalidReference - - b = p.parseWhitespace(b) +// offsetOf returns the offset of b within the parser's data. b must be a +// subslice of p.data. +func (p *Parser) offsetOf(b []byte) int { + return cap(p.data) - cap(b) +} - if len(b) == 0 { - return ref, b, nil +// rangeFrom returns the Range covering bytes from the start of `from` to the +// start of `to`. Both must be subslices of p.data. +func (p *Parser) rangeFrom(from, to []byte) Range { + start := p.offsetOf(from) + end := p.offsetOf(to) + return Range{ + Offset: uint32(start), //nolint:gosec // TOML documents are small + Length: uint32(end - start), //nolint:gosec // TOML documents are small } +} - if b[0] == '#' { - ref, rest, err := p.parseComment(b) - return ref, rest, err +// NextExpression parses the next top-level expression. If an expression was +// successfully parsed, it returns true. If the parser is at the end of the +// document or an error occurred, it returns false. +// +// Retrieve the parsed expression with Expression(). +func (p *Parser) NextExpression() bool { + if p.err != nil { + return false } - if b[0] == '\n' || b[0] == '\r' { - return ref, b, nil - } + p.nodes = p.nodes[:0] - var err error - if b[0] == '[' { - ref, b, err = p.parseTable(b) - } else { - ref, b, err = p.parseKeyval(b) - } + for { + b := skipWhitespace(p.left) + if len(b) == 0 { + p.left = b + return false + } - if err != nil { - return ref, nil, err + var err error + switch b[0] { + case '\n': + p.left = b[1:] + continue + case '\r': + if len(b) > 1 && b[1] == '\n' { + p.left = b[2:] + continue + } + err = NewParserError(b[:1], "expected newline but got %#U", b[0]) + case '#': + var comment, rest []byte + comment, rest, err = scanComment(b) + if err == nil { + rest, err = consumeEOL(rest) + } + if err == nil { + if p.KeepComments { + p.push(Node{ + Kind: Comment, + Raw: p.Range(comment), + Data: comment, + }) + p.left = rest + return true + } + p.left = rest + continue + } + case '[': + var rest []byte + rest, err = p.parseExprTable(b) + if err == nil { + p.left = rest + return true + } + default: + var rest []byte + rest, err = p.parseExprKeyval(b) + if err == nil { + p.left = rest + return true + } + } + + // Errors at the end of the input have an empty highlight. Extend + // them to the last byte of the input so that they carry a usable + // position. + var perr *ParserError + if errors.As(err, &perr) && len(perr.Highlight) == 0 { + if offset := p.offsetOf(perr.Highlight); offset > 0 && offset == len(p.data) { + perr.Highlight = p.data[offset-1 : offset] + } + } + + p.err = err + return false } +} - b = p.parseWhitespace(b) +// consumeEOL consumes a newline (LF or CRLF) or end of input. +func consumeEOL(b []byte) ([]byte, error) { + if len(b) == 0 { + return b, nil + } + switch b[0] { + case '\n': + return b[1:], nil + case '\r': + if len(b) > 1 && b[1] == '\n' { + return b[2:], nil + } + } + return nil, NewParserError(b[:1], "expected newline but got %#U", b[0]) +} +// finishLine handles `ws [comment] (newline|eof)` after a top-level +// expression. If a comment is present and KeepComments is set, it is attached +// as the next sibling of the expression's root node. +func (p *Parser) finishLine(root int32, b []byte) ([]byte, error) { + b = skipWhitespace(b) if len(b) > 0 && b[0] == '#' { - cref, rest, err := p.parseComment(b) - if cref != invalidReference { - p.builder.Chain(ref, cref) + comment, rest, err := scanComment(b) + if err != nil { + return nil, err + } + if p.KeepComments { + h := p.push(Node{ + Kind: Comment, + Raw: p.Range(comment), + Data: comment, + }) + p.at(root).next = h } - return ref, rest, err + b = rest } + return consumeEOL(b) +} - return ref, b, nil +// parseExprKeyval parses a top-level `key = value` expression, including its +// line termination. +func (p *Parser) parseExprKeyval(b []byte) ([]byte, error) { + root, rest, err := p.parseKeyval(b) + if err != nil { + return nil, err + } + return p.finishLine(root, rest) } -func (p *Parser) parseTable(b []byte) (reference, []byte, error) { - // table = std-table / array-table +// parseExprTable parses a `[table]` or `[[array table]]` expression, +// including its line termination. b starts at '['. +func (p *Parser) parseExprTable(b []byte) ([]byte, error) { + var root int32 + var err error + var rest []byte if len(b) > 1 && b[1] == '[' { - return p.parseArrayTable(b) + root, rest, err = p.parseArrayTableHeader(b) + } else { + root, rest, err = p.parseTableHeader(b) } - - return p.parseStdTable(b) + if err != nil { + return nil, err + } + return p.finishLine(root, rest) } -func (p *Parser) parseArrayTable(b []byte) (reference, []byte, error) { - // array-table = array-table-open key array-table-close - // array-table-open = %x5B.5B ws ; [[ Double left square bracket - // array-table-close = ws %x5D.5D ; ]] Double right square bracket - ref := p.builder.Push(Node{ - Kind: ArrayTable, - }) - - b = b[2:] - b = p.parseWhitespace(b) +// parseTableHeader parses `[ ws key ws ]`. b starts at '['. +func (p *Parser) parseTableHeader(b []byte) (int32, []byte, error) { + root := p.push(Node{Kind: Table}) - k, b, err := p.parseKey(b) + first, b, err := p.parseKey(skipWhitespace(b[1:])) if err != nil { - return ref, nil, err + return 0, nil, err } + p.at(root).child = first - p.builder.AttachChild(ref, k) - b = p.parseWhitespace(b) + if len(b) == 0 || b[0] != ']' { + return 0, nil, NewParserError(highlight1(b), "expected ']' to close table name") + } + return root, b[1:], nil +} - b, err = expect(']', b) +// parseArrayTableHeader parses `[[ ws key ws ]]`. b starts at '[['. +func (p *Parser) parseArrayTableHeader(b []byte) (int32, []byte, error) { + root := p.push(Node{Kind: ArrayTable}) + + first, b, err := p.parseKey(skipWhitespace(b[2:])) if err != nil { - return ref, nil, err + return 0, nil, err } + p.at(root).child = first - b, err = expect(']', b) - - return ref, b, err + if len(b) < 2 || b[0] != ']' || b[1] != ']' { + return 0, nil, NewParserError(highlight1(b), "expected ']]' to close array table name") + } + return root, b[2:], nil } -func (p *Parser) parseStdTable(b []byte) (reference, []byte, error) { - // std-table = std-table-open key std-table-close - // std-table-open = %x5B ws ; [ Left square bracket - // std-table-close = ws %x5D ; ] Right square bracket - ref := p.builder.Push(Node{ - Kind: Table, - }) - - b = b[1:] - b = p.parseWhitespace(b) +// parseKeyval parses `key keyval-sep val`. Returns the handle to the KeyValue +// node. +func (p *Parser) parseKeyval(b []byte) (int32, []byte, error) { + root := p.push(Node{Kind: KeyValue}) + start := b - key, b, err := p.parseKey(b) + firstKey, b, err := p.parseKey(b) if err != nil { - return ref, nil, err + return 0, nil, err } - p.builder.AttachChild(ref, key) - - b = p.parseWhitespace(b) + if len(b) == 0 || b[0] != '=' { + return 0, nil, NewParserError(highlight1(b), "expected '=' after key") + } + b = skipWhitespace(b[1:]) - b, err = expect(']', b) + value, b, err := p.parseVal(b) + if err != nil { + return 0, nil, err + } - return ref, b, err + p.at(root).child = value + p.at(value).next = firstKey + p.at(root).Raw = p.rangeFrom(start, b) + return root, b, nil } -func (p *Parser) parseKeyval(b []byte) (reference, []byte, error) { - // keyval = key keyval-sep val - ref := p.builder.Push(Node{ - Kind: KeyValue, - }) +// parseKey parses a potentially dotted key. It consumes the whitespace +// following the key, so that the caller can directly check for the next +// expected character ('=', ']', ...). Returns the handle of the first Key +// node; subsequent parts are chained via next. +func (p *Parser) parseKey(b []byte) (int32, []byte, error) { + var first, last int32 + for { + h, rest, err := p.parseSimpleKey(b) + if err != nil { + return 0, nil, err + } + if first == 0 { + first = h + } else { + p.at(last).next = h + } + last = h - key, b, err := p.parseKey(b) - if err != nil { - return invalidReference, nil, err + b = skipWhitespace(rest) + if len(b) > 0 && b[0] == '.' { + b = skipWhitespace(b[1:]) + continue + } + return first, b, nil } +} - // keyval-sep = ws %x3D ws ; = +func isUnquotedKeyChar(c byte) bool { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' +} - b = p.parseWhitespace(b) +// parseSimpleKey parses one key part: either a bare key or a quoted key. +func (p *Parser) parseSimpleKey(b []byte) (int32, []byte, error) { + raw, value, rest, err := p.scanSimpleKey(b) + if err != nil { + return 0, nil, err + } + h := p.push(Node{Kind: Key, Raw: p.Range(raw), Data: value}) + return h, rest, nil +} +// scanSimpleKey scans one key part (bare or quoted) without building an AST +// node. It returns the raw bytes, the decoded key value, and the rest of the +// input. +func (p *Parser) scanSimpleKey(b []byte) (raw, value, rest []byte, err error) { if len(b) == 0 { - return invalidReference, nil, NewParserError(b, "expected = after a key, but the document ends there") + return nil, nil, nil, NewParserError(b, "expected key but reached end of input") } - b, err = expect('=', b) - if err != nil { - return invalidReference, nil, err + switch b[0] { + case '\'': + return p.parseLiteralString(b) + case '"': + return p.parseBasicString(b) + default: + i := 0 + for i < len(b) && isUnquotedKeyChar(b[i]) { + i++ + } + if i == 0 { + return nil, nil, nil, NewParserError(b[:1], "invalid character at start of key: %#U", b[0]) + } + return b[:i], b[:i], b[i:], nil } +} - b = p.parseWhitespace(b) - - valRef, b, err := p.parseVal(b) - if err != nil { - return ref, b, err +// parseVal parses a TOML value and returns the handle to its node. +func (p *Parser) parseVal(b []byte) (int32, []byte, error) { + if len(b) == 0 { + return 0, nil, NewParserError(b, "expected value, not end of input") } - p.builder.Chain(valRef, key) - p.builder.AttachChild(ref, valRef) - - return ref, b, err + c := b[0] + switch { + case c == '"': + var raw, value, rest []byte + var err error + if len(b) > 2 && b[1] == '"' && b[2] == '"' { + raw, value, rest, err = p.parseMultilineBasicString(b) + } else { + raw, value, rest, err = p.parseBasicString(b) + } + if err != nil { + return 0, nil, err + } + h := p.push(Node{Kind: String, Raw: p.Range(raw), Data: value}) + return h, rest, nil + case c == '\'': + var raw, value, rest []byte + var err error + if len(b) > 2 && b[1] == '\'' && b[2] == '\'' { + raw, value, rest, err = p.parseMultilineLiteralString(b) + } else { + raw, value, rest, err = p.parseLiteralString(b) + } + if err != nil { + return 0, nil, err + } + h := p.push(Node{Kind: String, Raw: p.Range(raw), Data: value}) + return h, rest, nil + case c == 't': + return p.parseKeyword(b, "true", Bool) + case c == 'f': + return p.parseKeyword(b, "false", Bool) + case c == 'i': + return p.parseKeyword(b, "inf", Float) + case c == 'n': + return p.parseKeyword(b, "nan", Float) + case c == '[' || c == '{': + // Arrays and inline tables recurse back into parseVal for each of + // their elements. Bound that recursion so a document with millions of + // nested brackets or braces cannot overflow the goroutine stack. + if p.nesting >= maxValueNesting { + return 0, nil, NewParserError(b[:1], "arrays and inline tables are nested more than the maximum of %d levels deep", maxValueNesting) + } + p.nesting++ + var h int32 + var rest []byte + var err error + if c == '[' { + h, rest, err = p.parseValArray(b) + } else { + h, rest, err = p.parseInlineTable(b) + } + p.nesting-- + return h, rest, err + case c == '+' || c == '-': + return p.parseIntOrFloat(b) + case c >= '0' && c <= '9': + if isDateTimeStart(b) { + return p.parseDateTime(b) + } + return p.parseIntOrFloat(b) + default: + return 0, nil, NewParserError(b[:1], "unexpected character %#U at start of value", c) + } } -//nolint:cyclop,funlen -func (p *Parser) parseVal(b []byte) (reference, []byte, error) { - // val = string / boolean / array / inline-table / date-time / float / integer - ref := invalidReference - +// scanScalar scans a single scalar TOML value (string, integer, float, +// boolean, or date/time) without building any AST node. It returns the kind of +// the value, its raw bytes, its decoded value bytes (for strings: quotes +// removed and escapes resolved; identical to raw for the other kinds), and the +// rest of the input. Arrays and inline tables are not scalars and produce an +// error: use parseValue for those. +// +// It is exposed to the root toml package through internal/parserbridge for the +// fused generic-decode path; it is not part of the public API. +func (p *Parser) scanScalar(b []byte) (kind Kind, raw, value, rest []byte, err error) { if len(b) == 0 { - return ref, nil, NewParserError(b, "expected value, not eof") + return Invalid, nil, nil, nil, NewParserError(b, "expected value, not end of input") } - var err error c := b[0] - - switch c { - case '"': - var raw []byte - var v []byte - if scanFollowsMultilineBasicStringDelimiter(b) { - raw, v, b, err = p.parseMultilineBasicString(b) + switch { + case c == '"': + if len(b) > 2 && b[1] == '"' && b[2] == '"' { + raw, value, rest, err = p.parseMultilineBasicString(b) } else { - raw, v, b, err = p.parseBasicString(b) - } - - if err == nil { - ref = p.builder.Push(Node{ - Kind: String, - Raw: p.Range(raw), - Data: v, - }) + raw, value, rest, err = p.parseBasicString(b) } - - return ref, b, err - case '\'': - var raw []byte - var v []byte - if scanFollowsMultilineLiteralStringDelimiter(b) { - raw, v, b, err = p.parseMultilineLiteralString(b) + return String, raw, value, rest, err + case c == '\'': + if len(b) > 2 && b[1] == '\'' && b[2] == '\'' { + raw, value, rest, err = p.parseMultilineLiteralString(b) } else { - raw, v, b, err = p.parseLiteralString(b) + raw, value, rest, err = p.parseLiteralString(b) } - - if err == nil { - ref = p.builder.Push(Node{ - Kind: String, - Raw: p.Range(raw), - Data: v, - }) + return String, raw, value, rest, err + case c == 't': + return scanKeyword(b, "true", Bool) + case c == 'f': + return scanKeyword(b, "false", Bool) + case c == 'i': + return scanKeyword(b, "inf", Float) + case c == 'n': + return scanKeyword(b, "nan", Float) + case c == '+' || c == '-': + return scanIntOrFloat(b) + case c >= '0' && c <= '9': + if isDateTimeStart(b) { + return scanDateTime(b) } + return scanIntOrFloat(b) + default: + return Invalid, nil, nil, nil, NewParserError(b[:1], "unexpected character %#U at start of value", c) + } +} - return ref, b, err - case 't': - if !scanFollowsTrue(b) { - return ref, nil, NewParserError(atmost(b, 4), "expected 'true'") +// scanKey scans a potentially dotted key without building AST nodes, +// appending the decoded value of each part to dst (pass dst[:0] to reuse a +// buffer). It consumes the whitespace following the key, so the caller can +// directly check for the next expected character ('=', ']', ...). It returns +// the parts, the raw bytes spanning the whole key (from the first part to the +// end of the last one, excluding trailing whitespace, usable as an error +// highlight), the rest of the input, and any error. +// +// It is exposed to the root toml package through internal/parserbridge for the +// fused generic-decode path; it is not part of the public API. +func (p *Parser) scanKey(b []byte, dst [][]byte) (parts [][]byte, raw, rest []byte, err error) { + parts = dst + start := b + for { + _, value, r, err := p.scanSimpleKey(b) + if err != nil { + return nil, nil, nil, err } + parts = append(parts, value) - ref = p.builder.Push(Node{ - Kind: Bool, - Data: b[:4], - }) + // r points just past the current part: the key spans from start to + // here, ignoring any whitespace that follows. + raw = start[:len(start)-len(r)] - return ref, b[4:], nil - case 'f': - if !scanFollowsFalse(b) { - return ref, nil, NewParserError(atmost(b, 5), "expected 'false'") + b = skipWhitespace(r) + if len(b) > 0 && b[0] == '.' { + b = skipWhitespace(b[1:]) + continue } - - ref = p.builder.Push(Node{ - Kind: Bool, - Data: b[:5], - }) - - return ref, b[5:], nil - case '[': - return p.parseValArray(b) - case '{': - return p.parseInlineTable(b) - default: - return p.parseIntOrFloatOrDateTime(b) + return parts, raw, b, nil } } -func atmost(b []byte, n int) []byte { - if n >= len(b) { - return b +// parseValue parses a single TOML value, which may be an array or inline table, +// into the parser's arena. It returns the root node of the value and the rest +// of the input. It resets the arena, so any node returned by a previous call to +// parseValue, Expression, or NextExpression is invalidated. +// +// It is exposed to the root toml package through internal/parserbridge for the +// fused generic-decode path; it is not part of the public API. +func (p *Parser) parseValue(b []byte) (*Node, []byte, error) { + p.nodes = p.nodes[:0] + h, rest, err := p.parseVal(b) + if err != nil { + return nil, nil, err } - - return b[:n] + return &p.nodes[h-1], rest, nil } -func (p *Parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) { - v, rest, err := scanLiteralString(b) +func (p *Parser) parseKeyword(b []byte, kw string, kind Kind) (int32, []byte, error) { + k, raw, _, rest, err := scanKeyword(b, kw, kind) if err != nil { - return nil, nil, nil, err + return 0, nil, err } - - return v, v[1 : len(v)-1], rest, nil + h := p.push(Node{Kind: k, Raw: p.Range(raw), Data: raw}) + return h, rest, nil } -func (p *Parser) parseInlineTable(b []byte) (reference, []byte, error) { - // inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close - // inline-table-open = %x7B ws ; { - // inline-table-close = ws %x7D ; } - // inline-table-sep = ws %x2C ws ; , Comma - // inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] - parent := p.builder.Push(Node{ - Kind: InlineTable, - Raw: p.Range(b[:1]), - }) - - first := true - - var child reference +// scanKeyword scans a keyword value (true, false, inf, nan) without building +// an AST node. raw and value are identical (the keyword bytes). +func scanKeyword(b []byte, kw string, kind Kind) (Kind, []byte, []byte, []byte, error) { + if len(b) < len(kw) || string(b[:len(kw)]) != kw { + n := len(kw) + if len(b) < n { + n = len(b) + } + return Invalid, nil, nil, nil, NewParserError(b[:n], "expected keyword %q", kw) + } + return kind, b[:len(kw)], b[:len(kw)], b[len(kw):], nil +} +// parseValArray parses an array value. b starts at '['. +func (p *Parser) parseValArray(b []byte) (int32, []byte, error) { + arr := p.push(Node{Kind: Array}) b = b[1:] - var err error + var lastChild int32 + appendChild := func(h int32) { + if lastChild == 0 { + p.at(arr).child = h + } else { + p.at(lastChild).next = h + } + lastChild = h + } - for len(b) > 0 { - previousB := b - b = p.parseWhitespace(b) + // Comments inside the array are attached as follows: the first comment + // of a "run" (consecutive comments with no value in between) becomes a + // child of the array, interleaved with values; subsequent comments of the + // run are attached as children of the first one. + var runFirst, runLast int32 + // afterValue is true when a value has been parsed and a comma (or the + // closing bracket) is expected before the next one. + afterValue := false + for { + b = skipWhitespace(b) if len(b) == 0 { - return parent, nil, NewParserError(previousB[:1], "inline table is incomplete") - } - - if b[0] == '}' { - break + return 0, nil, NewParserError(b, "array is incomplete") } - if !first { - b, err = expect(',', b) + switch b[0] { + case ']': + return arr, b[1:], nil + case '\n': + b = b[1:] + continue + case '\r': + if len(b) > 1 && b[1] == '\n' { + b = b[2:] + continue + } + return 0, nil, NewParserError(b[:1], "expected newline but got %#U", b[0]) + case '#': + comment, rest, err := scanComment(b) if err != nil { - return parent, nil, err + return 0, nil, err } - b = p.parseWhitespace(b) - } - - var kv reference - - kv, b, err = p.parseKeyval(b) - if err != nil { - return parent, nil, err - } - - if first { - p.builder.AttachChild(parent, kv) - } else { - p.builder.Chain(child, kv) + if p.KeepComments { + h := p.push(Node{Kind: Comment, Raw: p.Range(comment), Data: comment}) + switch { + case runFirst == 0: + appendChild(h) + runFirst = h + case runLast == runFirst: + p.at(runFirst).child = h + default: + p.at(runLast).next = h + } + runLast = h + } + b = rest + continue + case ',': + if !afterValue { + return 0, nil, NewParserError(b[:1], "expected value but got %#U", b[0]) + } + afterValue = false + b = b[1:] + continue + default: + if afterValue { + return 0, nil, NewParserError(b[:1], "expected ',' or ']' after array value") + } + h, rest, err := p.parseVal(b) + if err != nil { + return 0, nil, err + } + appendChild(h) + afterValue = true + runFirst, runLast = 0, 0 + b = rest + continue } - child = kv - - first = false } - - rest, err := expect('}', b) - - return parent, rest, err } -//nolint:funlen,cyclop -func (p *Parser) parseValArray(b []byte) (reference, []byte, error) { - // array = array-open [ array-values ] ws-comment-newline array-close - // array-open = %x5B ; [ - // array-close = %x5D ; ] - // array-values = ws-comment-newline val ws-comment-newline array-sep array-values - // array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ] - // array-sep = %x2C ; , Comma - // ws-comment-newline = *( wschar / [ comment ] newline ) - arrayStart := b +// parseInlineTable parses an inline table value. b starts at '{'. +// +// Per TOML v1.1.0, inline tables may span multiple lines (whitespace, +// comments and newlines are allowed between elements) and may contain a +// trailing comma. +func (p *Parser) parseInlineTable(b []byte) (int32, []byte, error) { + tbl := p.push(Node{Kind: InlineTable, Raw: p.Range(b[:1])}) b = b[1:] - parent := p.builder.Push(Node{ - Kind: Array, - }) - - // First indicates whether the parser is looking for the first element - // (non-comment) of the array. - first := true - - lastChild := invalidReference - - addChild := func(valueRef reference) { - if lastChild == invalidReference { - p.builder.AttachChild(parent, valueRef) + var lastChild int32 + appendChild := func(h int32) { + if lastChild == 0 { + p.at(tbl).child = h } else { - p.builder.Chain(lastChild, valueRef) + p.at(lastChild).next = h } - lastChild = valueRef + lastChild = h } - var err error - for len(b) > 0 { - cref := invalidReference - cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) - if err != nil { - return parent, nil, err - } - - if cref != invalidReference { - addChild(cref) - } + // Comments are attached as in arrays: the first comment of a "run" + // (consecutive comments with no key-value in between) becomes a child of + // the table, interleaved with key-values; subsequent comments of the run + // hang off the first one. + var runFirst, runLast int32 + // afterValue is true when a key-value has been parsed and a comma (or the + // closing brace) is expected before the next one. + afterValue := false + for { + b = skipWhitespace(b) if len(b) == 0 { - return parent, nil, NewParserError(arrayStart[:1], "array is incomplete") - } - - if b[0] == ']' { - break + return 0, nil, NewParserError(b, "inline table is incomplete") } - if b[0] == ',' { - if first { - return parent, nil, NewParserError(b[0:1], "array cannot start with comma") - } + switch b[0] { + case '}': + return tbl, b[1:], nil + case '\n': b = b[1:] - - cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) + continue + case '\r': + if len(b) > 1 && b[1] == '\n' { + b = b[2:] + continue + } + return 0, nil, NewParserError(b[:1], "expected newline but got %#U", b[0]) + case '#': + comment, rest, err := scanComment(b) if err != nil { - return parent, nil, err + return 0, nil, err } - if cref != invalidReference { - addChild(cref) + if p.KeepComments { + h := p.push(Node{Kind: Comment, Raw: p.Range(comment), Data: comment}) + switch { + case runFirst == 0: + appendChild(h) + runFirst = h + case runLast == runFirst: + p.at(runFirst).child = h + default: + p.at(runLast).next = h + } + runLast = h } - } else if !first { - return parent, nil, NewParserError(b[0:1], "array elements must be separated by commas") - } - - // TOML allows trailing commas in arrays. - if len(b) > 0 && b[0] == ']' { - break - } - - var valueRef reference - valueRef, b, err = p.parseVal(b) - if err != nil { - return parent, nil, err + b = rest + continue + case ',': + if !afterValue { + return 0, nil, NewParserError(b[:1], "unexpected comma in inline table") + } + afterValue = false + b = b[1:] + continue + default: + if afterValue { + return 0, nil, NewParserError(b[:1], "expected ',' or '}' after inline table key-value") + } + h, rest, err := p.parseKeyval(b) + if err != nil { + return 0, nil, err + } + appendChild(h) + afterValue = true + runFirst, runLast = 0, 0 + b = rest + continue } + } +} - addChild(valueRef) +func isDigit(c byte) bool { + return c >= '0' && c <= '9' +} - cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) - if err != nil { - return parent, nil, err - } - if cref != invalidReference { - addChild(cref) - } +func isHexDigit(c byte) bool { + return isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') +} - first = false +// isDateTimeStart reports whether b looks like the start of a date or time +// value instead of a number. Values starting with two digits followed by a +// colon are times; values starting with four digits followed by a dash are +// dates. +func isDateTimeStart(b []byte) bool { + if len(b) >= 3 && isDigit(b[1]) && b[2] == ':' { + return true } - - rest, err := expect(']', b) - - return parent, rest, err + if len(b) >= 5 && isDigit(b[1]) && isDigit(b[2]) && isDigit(b[3]) && b[4] == '-' { + return true + } + return false } -func (p *Parser) parseOptionalWhitespaceCommentNewline(b []byte) (reference, []byte, error) { - rootCommentRef := invalidReference - latestCommentRef := invalidReference +// expectDigits checks that the n first bytes of b are digits. +// parseDateTime parses date and/or time values. b starts with a digit. +// +// The parser is lenient: it scans the characters that can be part of a date +// and/or time value to delimit and classify the token, but leaves the +// validation of its contents to the document consumer. This keeps the value +// in one piece, so that errors about its content can point at the right +// place. +func (p *Parser) parseDateTime(b []byte) (int32, []byte, error) { + kind, raw, _, rest, err := scanDateTime(b) + if err != nil { + return 0, nil, err + } + h := p.push(Node{Kind: kind, Raw: p.Range(raw), Data: raw}) + return h, rest, nil +} - addComment := func(ref reference) { - if rootCommentRef == invalidReference { - rootCommentRef = ref - } else if latestCommentRef == invalidReference { - p.builder.AttachChild(rootCommentRef, ref) - latestCommentRef = ref - } else { - p.builder.Chain(latestCommentRef, ref) - latestCommentRef = ref +// scanDateTime classifies and delimits a date/time value without building an +// AST node. raw and value are identical (the token bytes). +func scanDateTime(b []byte) (Kind, []byte, []byte, []byte, error) { + // Greedily scan the characters that may compose a date/time value. A + // space is part of the value only when it serves as the delimiter + // between the date and the time, which is approximated by requiring a + // digit right after it. + i := 0 + delim := -1 + for i < len(b) { + c := b[i] + if isDigit(c) || c == ':' || c == '-' || c == '+' || c == '.' || c == 'Z' || c == 'z' { + i++ + continue + } + if c == 'T' || c == 't' || (c == ' ' && i+1 < len(b) && isDigit(b[i+1])) { + if delim < 0 { + delim = i + } + i++ + continue } + break } + tok := b[:i] - for len(b) > 0 { - var err error - b = p.parseWhitespace(b) + var kind Kind + switch { + case tok[2] == ':': + kind = LocalTime + case delim < 0: + kind = LocalDate + case bytes.ContainsAny(tok[delim+1:], "Zz+-"): + kind = DateTime + default: + kind = LocalDateTime + } - if len(b) > 0 && b[0] == '#' { - var ref reference - ref, b, err = p.parseComment(b) - if err != nil { - return invalidReference, nil, err - } - if ref != invalidReference { - addComment(ref) - } - } + return kind, tok, tok, b[i:], nil +} - if len(b) == 0 { - break +// scanDigitsWithUnderscores scans a run of digits potentially separated by +// underscores. b starts right after the first digit of the run. isInRange +// selects the kind of digits. Returns the index after the run. +func scanDigitsWithUnderscores(b []byte, i int, isInRange func(byte) bool) (int, error) { + for i < len(b) { + c := b[i] + if isInRange(c) { + i++ + continue } - - if b[0] == '\n' || b[0] == '\r' { - b, err = p.parseNewline(b) - if err != nil { - return invalidReference, nil, err + if c == '_' { + if i+1 >= len(b) || !isInRange(b[i+1]) { + end := i + 2 + if end > len(b) { + end = len(b) + } + return 0, NewParserError(b[i:end], "number must have at least one digit between underscores") } - } else { - break + i += 2 + continue } + break } - - return rootCommentRef, b, nil + return i, nil } -func (p *Parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) { - token, rest, err := scanMultilineLiteralString(b) +// parseIntOrFloat parses integer and float values, including the special +// values inf and nan with an optional sign. +func (p *Parser) parseIntOrFloat(b []byte) (int32, []byte, error) { + kind, raw, _, rest, err := scanIntOrFloat(b) if err != nil { - return nil, nil, nil, err + return 0, nil, err } + h := p.push(Node{Kind: kind, Raw: p.Range(raw), Data: raw}) + return h, rest, nil +} - i := 3 - - // skip the immediate new line - if token[i] == '\n' { +// scanIntOrFloat delimits and classifies an integer or float value (including +// the special floats inf and nan with an optional sign) without building an +// AST node. raw and value are identical (the token bytes). +func scanIntOrFloat(b []byte) (Kind, []byte, []byte, []byte, error) { + i := 0 + if b[i] == '+' || b[i] == '-' { i++ - } else if token[i] == '\r' && token[i+1] == '\n' { - i += 2 + } + if i >= len(b) { + return Invalid, nil, nil, nil, NewParserError(b, "expected number after sign") } - return token, token[i : len(token)-3], rest, err -} + // special floats + if b[i] == 'i' || b[i] == 'n' { + kw := "inf" + if b[i] == 'n' { + kw = "nan" + } + if len(b) < i+3 || string(b[i:i+3]) != kw { + return Invalid, nil, nil, nil, NewParserError(b[i:i+1], "expected %q", kw) + } + i += 3 + return Float, b[:i], b[:i], b[i:], nil + } -//nolint:funlen,gocognit,cyclop -func (p *Parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, error) { - // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body - // ml-basic-string-delim - // ml-basic-string-delim = 3quotation-mark - // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] - // - // mlb-content = mlb-char / newline / mlb-escaped-nl - // mlb-char = mlb-unescaped / escaped - // mlb-quotes = 1*2quotation-mark - // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii - // mlb-escaped-nl = escape ws newline *( wschar / newline ) - token, escaped, rest, err := scanMultilineBasicString(b) + if !isDigit(b[i]) { + return Invalid, nil, nil, nil, NewParserError(b[i:i+1], "expected digit but got %#U", b[i]) + } + + // radix prefixes + if b[i] == '0' && i+1 < len(b) && (b[i+1] == 'x' || b[i+1] == 'o' || b[i+1] == 'b') { + if i != 0 { + return Invalid, nil, nil, nil, NewParserError(b[:2], "sign is not allowed on numbers with a radix prefix") + } + var isInRange func(byte) bool + switch b[1] { + case 'x': + isInRange = isHexDigit + case 'o': + isInRange = func(c byte) bool { return c >= '0' && c <= '7' } + case 'b': + isInRange = func(c byte) bool { return c == '0' || c == '1' } + } + i = 2 + if i >= len(b) || !isInRange(b[i]) { + return Invalid, nil, nil, nil, NewParserError(b[:2], "radix prefix must be followed by at least one digit") + } + i++ + var err error + i, err = scanDigitsWithUnderscores(b, i, isInRange) + if err != nil { + return Invalid, nil, nil, nil, err + } + return Integer, b[:i], b[:i], b[i:], nil + } + + // decimal integer part + leadingZero := b[i] == '0' + digitsStart := i + i++ + var err error + i, err = scanDigitsWithUnderscores(b, i, isDigit) if err != nil { - return nil, nil, nil, err + return Invalid, nil, nil, nil, err + } + if leadingZero && i > digitsStart+1 { + return Invalid, nil, nil, nil, NewParserError(b[digitsStart:digitsStart+2], "integers cannot have leading zeroes") } - i := 3 + kind := Integer - // skip the immediate new line - if token[i] == '\n' { + // fractional part + if i < len(b) && b[i] == '.' { i++ - } else if token[i] == '\r' && token[i+1] == '\n' { - i += 2 + if i >= len(b) || !isDigit(b[i]) { + return Invalid, nil, nil, nil, NewParserError(highlight1(b[i:]), "decimal point must be followed by a digit") + } + i++ + i, err = scanDigitsWithUnderscores(b, i, isDigit) + if err != nil { + return Invalid, nil, nil, nil, err + } + kind = Float } - // fast path - startIdx := i - endIdx := len(token) - len(`"""`) + // exponent + if i < len(b) && (b[i] == 'e' || b[i] == 'E') { + i++ + if i < len(b) && (b[i] == '+' || b[i] == '-') { + i++ + } + if i >= len(b) || !isDigit(b[i]) { + return Invalid, nil, nil, nil, NewParserError(highlight1(b[i:]), "exponent must contain at least one digit") + } + i++ + i, err = scanDigitsWithUnderscores(b, i, isDigit) + if err != nil { + return Invalid, nil, nil, nil, err + } + kind = Float + } - if !escaped { - str := token[startIdx:endIdx] - verr := characters.Utf8TomlValidAlreadyEscaped(str) - if verr.Zero() { - return token, str, rest, nil + // A letter right after the number means it was meant to be a string that + // was left unquoted (e.g. "20s"). Report that instead of the misleading + // "expected newline" raised later (issue #413). + if i < len(b) { + if c := b[i]; (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') { + return Invalid, nil, nil, nil, NewParserError(b[i:i+1], "strings must be quoted") } - return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") } - var builder bytes.Buffer + return kind, b[:i], b[:i], b[i:], nil +} - // The scanner ensures that the token starts and ends with quotes and that - // escapes are balanced. - for i < len(token)-3 { - c := token[i] +// highlight1 returns a 1-byte highlight at the start of b, or b itself if it +// is empty. +func highlight1(b []byte) []byte { + if len(b) > 0 { + return b[:1] + } + return b +} - //nolint:nestif - if c == '\\' { - // When the last non-whitespace character on a line is an unescaped \, - // it will be trimmed along with all whitespace (including newlines) up - // to the next non-whitespace character or closing delimiter. +func skipWhitespace(b []byte) []byte { + for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') { + b = b[1:] + } + return b +} - isLastNonWhitespaceOnLine := false - j := 1 - findEOLLoop: - for ; j < len(token)-3-i; j++ { - switch token[i+j] { - case ' ', '\t': - continue - case '\r': - if token[i+j+1] == '\n' { - continue - } - case '\n': - isLastNonWhitespaceOnLine = true - } - break findEOLLoop - } - if isLastNonWhitespaceOnLine { - i += j - for ; i < len(token)-3; i++ { - c := token[i] - if !(c == '\n' || c == '\r' || c == ' ' || c == '\t') { - i-- - break - } - } - i++ - continue - } +// Word-at-a-time byte scanning helpers. These detect, within an 8-byte word, +// the presence of bytes that need special handling, so that runs of plain +// ASCII characters can be skipped 8 bytes at a time. +const ( + lsb = 0x0101010101010101 + msb = 0x8080808080808080 +) - // handle escaping - i++ - c = token[i] +// hasByteBelow reports whether any byte of the word x is strictly below n. +// Only meaningful when combined with a check that no byte has its high bit +// set. +func hasByteBelow(x uint64, n uint64) uint64 { + return (x - n*lsb) & ^x & msb +} - switch c { - case '"', '\\': - builder.WriteByte(c) - case 'b': - builder.WriteByte('\b') - case 'f': - builder.WriteByte('\f') - case 'n': - builder.WriteByte('\n') - case 'r': - builder.WriteByte('\r') - case 't': - builder.WriteByte('\t') - case 'e': - builder.WriteByte(0x1B) - case 'u': - x, err := hexToRune(atmost(token[i+1:], 4), 4) - if err != nil { - return nil, nil, nil, err - } - builder.WriteRune(x) - i += 4 - case 'U': - x, err := hexToRune(atmost(token[i+1:], 8), 8) - if err != nil { - return nil, nil, nil, err - } +// hasByteEqual reports whether any byte of the word x equals c. Only +// meaningful for bytes without their high bit set. +func hasByteEqual(x uint64, c uint64) uint64 { + y := x ^ (c * lsb) + return (y - lsb) & ^y & msb +} - builder.WriteRune(x) - i += 8 - default: - return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c) +// scanComment parses a comment, starting at the '#' character. It returns the +// comment bytes (including '#', excluding the line ending) and the rest of +// the input. +func scanComment(b []byte) ([]byte, []byte, error) { + i := 1 + for i < len(b) { + // Fast path: skip 8 bytes at a time as long as they are all plain + // printable ASCII. + for i+8 <= len(b) { + x := binary.LittleEndian.Uint64(b[i:]) + if (x&msb)|hasByteBelow(x, 0x20)|hasByteEqual(x, 0x7f) != 0 { + break + } + i += 8 + } + if i >= len(b) { + break + } + + c := b[i] + if c >= 0x80 { + var ok bool + i, ok = scanUtf8Run(b, i) + if !ok { + return nil, nil, NewParserError(b[i:i+1], "invalid UTF-8 character in comment") } + continue + } + switch { + case c >= 0x20 && c < 0x7f: i++ - } else { - size := characters.Utf8ValidNext(token[i:]) - if size == 0 { - return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c) + case c == '\n': + return b[:i], b[i:], nil + case c == '\r': + if i+1 < len(b) && b[i+1] == '\n' { + return b[:i], b[i:], nil } - builder.Write(token[i : i+size]) - i += size + return nil, nil, NewParserError(b[i:i+1], "carriage returns are not allowed in comments") + case c == '\t': + i++ + default: + return nil, nil, NewParserError(b[i:i+1], "control characters are not allowed in comments") } } - - return token, builder.Bytes(), rest, nil + return b[:i], b[i:], nil } -func (p *Parser) parseKey(b []byte) (reference, []byte, error) { - // key = simple-key / dotted-key - // simple-key = quoted-key / unquoted-key - // - // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ - // quoted-key = basic-string / literal-string - // dotted-key = simple-key 1*( dot-sep simple-key ) - // - // dot-sep = ws %x2E ws ; . Period - raw, key, b, err := p.parseSimpleKey(b) - if err != nil { - return invalidReference, nil, err - } - - ref := p.builder.Push(Node{ - Kind: Key, - Raw: p.Range(raw), - Data: key, - }) - +// parseLiteralString parses a single-line literal string, starting at the +// opening quote. Returns the raw bytes (with quotes), the string value +// (without quotes) and the rest of the input. +func (p *Parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) { + i := 1 for { - b = p.parseWhitespace(b) - if len(b) > 0 && b[0] == '.' { - b = p.parseWhitespace(b[1:]) - - raw, key, b, err = p.parseSimpleKey(b) - if err != nil { - return ref, nil, err + // Fast path over plain ASCII. + for i+8 <= len(b) { + x := binary.LittleEndian.Uint64(b[i:]) + if (x&msb)|hasByteBelow(x, 0x20)|hasByteEqual(x, '\'')|hasByteEqual(x, 0x7f) != 0 { + break } + i += 8 + } + if i >= len(b) { + return nil, nil, nil, NewParserError(b[len(b):], "unterminated literal string") + } - p.builder.PushAndChain(Node{ - Kind: Key, - Raw: p.Range(raw), - Data: key, - }) - } else { - break + c := b[i] + switch { + case c == '\'': + return b[:i+1], b[1:i], b[i+1:], nil + case c >= 0x20 && c < 0x7f: + i++ + case c == '\t': + i++ + case c == '\n' || c == '\r': + return nil, nil, nil, NewParserError(b[i:i+1], "literal strings cannot have new lines") + case c < 0x80: + return nil, nil, nil, NewParserError(b[i:i+1], "literal strings cannot have control characters") + default: + var ok bool + i, ok = scanUtf8Run(b, i) + if !ok { + return nil, nil, nil, NewParserError(b[i:i+1], "invalid UTF-8 character in literal string") + } } } - - return ref, b, nil } -func (p *Parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) { - if len(b) == 0 { - return nil, nil, nil, NewParserError(b, "expected key but found none") +// parseMultilineLiteralString parses a multi-line literal string, starting at +// the opening triple quote. +func (p *Parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) { + i := 3 + // trim the newline right after the opening delimiter + if i < len(b) && b[i] == '\n' { + i++ + } else if i+1 < len(b) && b[i] == '\r' && b[i+1] == '\n' { + i += 2 } + contentStart := i - // simple-key = quoted-key / unquoted-key - // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ - // quoted-key = basic-string / literal-string - switch { - case b[0] == '\'': - return p.parseLiteralString(b) - case b[0] == '"': - return p.parseBasicString(b) - case isUnquotedKeyChar(b[0]): - key, rest = scanUnquotedKey(b) - return key, key, rest, nil - default: - return nil, nil, nil, NewParserError(b[0:1], "invalid character at start of key: %c", b[0]) + for i < len(b) { + c := b[i] + switch { + case c == '\'': + // count consecutive quotes + j := i + for j < len(b) && b[j] == '\'' { + j++ + } + n := j - i + if n >= 3 { + if n > 5 { + return nil, nil, nil, NewParserError(b[i:j], "too many quotes at the end of a multiline literal string") + } + // n-3 quotes belong to the content; the last 3 close the + // string. + contentEnd := i + n - 3 + return b[:j], b[contentStart:contentEnd], b[j:], nil + } + i = j + case c >= 0x20 && c < 0x7f: + i++ + case c == '\t' || c == '\n': + i++ + case c == '\r': + if i+1 < len(b) && b[i+1] == '\n' { + i += 2 + continue + } + return nil, nil, nil, NewParserError(b[i:i+1], "carriage returns must be followed by a newline character") + case c < 0x80: + return nil, nil, nil, NewParserError(b[i:i+1], "multiline literal strings cannot have control characters") + default: + var ok bool + i, ok = scanUtf8Run(b, i) + if !ok { + return nil, nil, nil, NewParserError(b[i:i+1], "invalid UTF-8 character in multiline literal string") + } + } } + return nil, nil, nil, NewParserError(b[len(b):], "multiline literal string not terminated by '''") } -//nolint:funlen,cyclop +// parseBasicString parses a single-line basic string, starting at the opening +// quote. The value is a subslice of the input if the string contains no +// escape sequence, or a new allocation otherwise. func (p *Parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { - // basic-string = quotation-mark *basic-char quotation-mark - // quotation-mark = %x22 ; " - // basic-char = basic-unescaped / escaped - // basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii - // escaped = escape escape-seq-char - // escape-seq-char = %x22 ; " quotation mark U+0022 - // escape-seq-char =/ %x5C ; \ reverse solidus U+005C - // escape-seq-char =/ %x62 ; b backspace U+0008 - // escape-seq-char =/ %x66 ; f form feed U+000C - // escape-seq-char =/ %x6E ; n line feed U+000A - // escape-seq-char =/ %x72 ; r carriage return U+000D - // escape-seq-char =/ %x74 ; t tab U+0009 - // escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX - // escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX - token, escaped, rest, err := scanBasicString(b) - if err != nil { - return nil, nil, nil, err - } - - startIdx := len(`"`) - endIdx := len(token) - len(`"`) + i := 1 + // First pass: handle strings without escape sequences without allocating. + for { + for i+8 <= len(b) { + x := binary.LittleEndian.Uint64(b[i:]) + if (x&msb)|hasByteBelow(x, 0x20)|hasByteEqual(x, '"')|hasByteEqual(x, '\\')|hasByteEqual(x, 0x7f) != 0 { + break + } + i += 8 + } + if i >= len(b) { + return nil, nil, nil, NewParserError(b[len(b):], "unterminated basic string") + } - // Fast path. If there is no escape sequence, the string should just be - // an UTF-8 encoded string, which is the same as Go. In that case, - // validate the string and return a direct reference to the buffer. - if !escaped { - str := token[startIdx:endIdx] - verr := characters.Utf8TomlValidAlreadyEscaped(str) - if verr.Zero() { - return token, str, rest, nil + c := b[i] + switch { + case c == '"': + return b[:i+1], b[1:i], b[i+1:], nil + case c == '\\': + // switch to the escape-aware parser, copying what has been + // scanned so far + return p.parseBasicStringEscaped(b, i) + case c >= 0x20 && c < 0x7f: + i++ + case c == '\t': + i++ + case c == '\n' || c == '\r': + return nil, nil, nil, NewParserError(b[i:i+1], "basic strings cannot have new lines") + case c < 0x80: + return nil, nil, nil, NewParserError(b[i:i+1], "basic strings cannot have control characters") + default: + var ok bool + i, ok = scanUtf8Run(b, i) + if !ok { + return nil, nil, nil, NewParserError(b[i:i+1], "invalid UTF-8 character in basic string") + } } - return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") } +} - i := startIdx - - var builder bytes.Buffer - - // The scanner ensures that the token starts and ends with quotes and that - // escapes are balanced. - for i < len(token)-1 { - c := token[i] - if c == '\\' { +// findBasicStringEnd returns the index of the quote closing a basic string, +// or -1 if the string is not terminated. i is the index of the first +// character after the opening quote. It does not validate the content: it +// only skips over escape sequences so that escaped quotes do not terminate +// the string. +func findBasicStringEnd(b []byte, i int) int { + for i < len(b) { + switch b[i] { + case '"': + return i + case '\\': + i += 2 + default: i++ - c = token[i] - - switch c { - case '"', '\\': - builder.WriteByte(c) - case 'b': - builder.WriteByte('\b') - case 'f': - builder.WriteByte('\f') - case 'n': - builder.WriteByte('\n') - case 'r': - builder.WriteByte('\r') - case 't': - builder.WriteByte('\t') - case 'e': - builder.WriteByte(0x1B) - case 'u': - x, err := hexToRune(token[i+1:len(token)-1], 4) - if err != nil { - return nil, nil, nil, err - } + } + } + return -1 +} - builder.WriteRune(x) - i += 4 - case 'U': - x, err := hexToRune(token[i+1:len(token)-1], 8) - if err != nil { - return nil, nil, nil, err - } +// parseBasicStringEscaped continues parsing a basic string that contains +// escape sequences. i is the index of the first backslash. +func (p *Parser) parseBasicStringEscaped(b []byte, i int) ([]byte, []byte, []byte, error) { + // Escape sequences only ever shrink, so the content length before + // unescaping is enough to never reallocate. + bufCap := len(b) - 1 + if end := findBasicStringEnd(b, i); end >= 0 { + bufCap = end - 1 + } + buf := make([]byte, i-1, bufCap) + copy(buf, b[1:i]) - builder.WriteRune(x) - i += 8 - default: - return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c) + for i < len(b) { + c := b[i] + switch { + case c == '"': + return b[:i+1], buf, b[i+1:], nil + case c == '\\': + i++ + if i >= len(b) { + return nil, nil, nil, NewParserError(b[i-1:], `need a character after \`) } + var err error + buf, i, err = unescape(buf, b, i) + if err != nil { + return nil, nil, nil, err + } + case c >= 0x20 && c < 0x7f: + buf = append(buf, c) i++ - } else { - size := characters.Utf8ValidNext(token[i:]) - if size == 0 { - return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c) + case c == '\t': + buf = append(buf, c) + i++ + case c == '\n' || c == '\r': + return nil, nil, nil, NewParserError(b[i:i+1], "basic strings cannot have new lines") + case c < 0x80: + return nil, nil, nil, NewParserError(b[i:i+1], "basic strings cannot have control characters") + default: + j, ok := scanUtf8Run(b, i) + if !ok { + return nil, nil, nil, NewParserError(b[i:i+1], "invalid UTF-8 character in basic string") } - builder.Write(token[i : i+size]) - i += size + buf = append(buf, b[i:j]...) + i = j } } - - return token, builder.Bytes(), rest, nil + return nil, nil, nil, NewParserError(b[len(b):], "unterminated basic string") } -func hexToRune(b []byte, length int) (rune, error) { - if len(b) < length { - return -1, NewParserError(b, "unicode point needs %d character, not %d", length, len(b)) +// unescape processes one escape sequence. i is the index of the character +// right after the backslash. It returns the updated buffer and index. +func unescape(buf []byte, b []byte, i int) ([]byte, int, error) { + c := b[i] + switch c { + case '"': + return append(buf, '"'), i + 1, nil + case '\\': + return append(buf, '\\'), i + 1, nil + case 'b': + return append(buf, '\b'), i + 1, nil + case 'f': + return append(buf, '\f'), i + 1, nil + case 'n': + return append(buf, '\n'), i + 1, nil + case 'r': + return append(buf, '\r'), i + 1, nil + case 't': + return append(buf, '\t'), i + 1, nil + case 'e': + // TOML v1.1.0: \e is the escape character (U+001B). + return append(buf, 0x1B), i + 1, nil + case 'x': + // TOML v1.1.0: \xHH is a two-digit hexadecimal code point. + return unescapeUnicode(buf, b, i+1, 2) + case 'u': + return unescapeUnicode(buf, b, i+1, 4) + case 'U': + return unescapeUnicode(buf, b, i+1, 8) + default: + return nil, 0, NewParserError(b[i-1:i+1], "invalid escape character %#U", c) } - b = b[:length] +} +// unescapeUnicode handles \uXXXX and \UXXXXXXXX escape sequences. i is the +// index of the first hex digit. +func unescapeUnicode(buf []byte, b []byte, i int, n int) ([]byte, int, error) { + if i+n > len(b) { + return nil, 0, NewParserError(b[i-2:], "unicode escape sequence is too short") + } var r uint32 - for i, c := range b { - d := uint32(0) + for k := 0; k < n; k++ { + c := b[i+k] + var d uint32 switch { - case '0' <= c && c <= '9': + case c >= '0' && c <= '9': d = uint32(c - '0') - case 'a' <= c && c <= 'f': - d = uint32(c - 'a' + 10) - case 'A' <= c && c <= 'F': - d = uint32(c - 'A' + 10) + case c >= 'a' && c <= 'f': + d = uint32(c-'a') + 10 + case c >= 'A' && c <= 'F': + d = uint32(c-'A') + 10 default: - return -1, NewParserError(b[i:i+1], "non-hex character") + return nil, 0, NewParserError(b[i+k:i+k+1], "invalid hexadecimal digit in unicode escape sequence") } - r = r*16 + d + r = r<<4 | d } - - if r > unicode.MaxRune || 0xD800 <= r && r < 0xE000 { - return -1, NewParserError(b, "escape sequence is invalid Unicode code point") + if r > utf8.MaxRune || (r >= 0xD800 && r <= 0xDFFF) { + return nil, 0, NewParserError(b[i-2:i+n], "escape sequence is not a valid unicode code point") } - - return rune(r), nil -} - -func (p *Parser) parseWhitespace(b []byte) []byte { - // ws = *wschar - // wschar = %x20 ; Space - // wschar =/ %x09 ; Horizontal tab - _, rest := scanWhitespace(b) - - return rest + return utf8.AppendRune(buf, rune(r)), i + n, nil } -//nolint:cyclop -func (p *Parser) parseIntOrFloatOrDateTime(b []byte) (reference, []byte, error) { - switch b[0] { - case 'i': - if !scanFollowsInf(b) { - return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'inf'") - } - - return p.builder.Push(Node{ - Kind: Float, - Data: b[:3], - Raw: p.Range(b[:3]), - }), b[3:], nil - case 'n': - if !scanFollowsNan(b) { - return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'nan'") - } - - return p.builder.Push(Node{ - Kind: Float, - Data: b[:3], - Raw: p.Range(b[:3]), - }), b[3:], nil - case '+', '-': - return p.scanIntOrFloat(b) - } - - if len(b) < 3 { - return p.scanIntOrFloat(b) +// parseMultilineBasicString parses a multi-line basic string, starting at the +// opening triple quote. +func (p *Parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, error) { + i := 3 + // trim the newline right after the opening delimiter + if i < len(b) && b[i] == '\n' { + i++ + } else if i+1 < len(b) && b[i] == '\r' && b[i+1] == '\n' { + i += 2 } + contentStart := i - s := 5 - if len(b) < s { - s = len(b) + // First pass without allocating, until an escape sequence is found. + for i < len(b) { + c := b[i] + switch { + case c == '"': + j := i + for j < len(b) && b[j] == '"' { + j++ + } + n := j - i + if n >= 3 { + if n > 5 { + return nil, nil, nil, NewParserError(b[i:j], "too many quotes at the end of a multiline basic string") + } + contentEnd := i + n - 3 + return b[:j], b[contentStart:contentEnd], b[j:], nil + } + i = j + case c == '\\': + return p.parseMultilineBasicStringEscaped(b, contentStart, i) + case c >= 0x20 && c < 0x7f: + i++ + case c == '\t' || c == '\n': + i++ + case c == '\r': + if i+1 < len(b) && b[i+1] == '\n' { + i += 2 + continue + } + return nil, nil, nil, NewParserError(b[i:i+1], "carriage returns must be followed by a newline character") + case c < 0x80: + return nil, nil, nil, NewParserError(b[i:i+1], "multiline basic strings cannot have control characters") + default: + var ok bool + i, ok = scanUtf8Run(b, i) + if !ok { + return nil, nil, nil, NewParserError(b[i:i+1], "invalid UTF-8 character in multiline basic string") + } + } } + return nil, nil, nil, NewParserError(b[len(b):], `multiline basic string not terminated by """`) +} - for idx, c := range b[:s] { - if isDigit(c) { +// findMultilineBasicStringEnd returns the index of the first quote of the +// run of quotes closing a multi-line basic string, or -1 if the string is +// not terminated. It does not validate the content: it only skips over +// escape sequences so that escaped quotes do not terminate the string. +func findMultilineBasicStringEnd(b []byte, i int) int { + for { + j := bytes.IndexAny(b[i:], "\"\\") + if j < 0 { + return -1 + } + i += j + if b[i] == '\\' { + i += 2 + if i > len(b) { + return -1 + } continue } - - if idx == 2 && c == ':' || (idx == 4 && c == '-') { - return p.scanDateTime(b) + j = i + for j < len(b) && b[j] == '"' { + j++ } - - break + if j-i >= 3 { + return i + } + i = j } - - return p.scanIntOrFloat(b) } -func (p *Parser) scanDateTime(b []byte) (reference, []byte, error) { - // scans for contiguous characters in [0-9T:Z.+-], and up to one space if - // followed by a digit. - hasDate := false - hasTime := false - hasTz := false - seenSpace := false +// parseMultilineBasicStringEscaped continues parsing a multi-line basic +// string that contains escape sequences. i is the index of the first +// backslash; content starts at contentStart. +func (p *Parser) parseMultilineBasicStringEscaped(b []byte, contentStart, i int) ([]byte, []byte, []byte, error) { + // Escape sequences only ever shrink, so the content length before + // unescaping is enough to never reallocate. The closing run of quotes + // can lend up to two quotes to the content. + bufCap := len(b) - contentStart + if end := findMultilineBasicStringEnd(b, i); end >= 0 { + bufCap = end + 2 - contentStart + } + buf := make([]byte, i-contentStart, bufCap) + copy(buf, b[contentStart:i]) - i := 0 -byteLoop: - for ; i < len(b); i++ { + for i < len(b) { c := b[i] - switch { - case isDigit(c): - case c == '-': - hasDate = true - const minOffsetOfTz = 8 - if i >= minOffsetOfTz { - hasTz = true + case c == '"': + j := i + for j < len(b) && b[j] == '"' { + j++ } - case c == 'T' || c == 't' || c == ':' || c == '.': - hasTime = true - case c == '+' || c == '-' || c == 'Z' || c == 'z': - hasTz = true - case c == ' ': - if !seenSpace && i+1 < len(b) && isDigit(b[i+1]) { - i += 2 - // Avoid reaching past the end of the document in case the time - // is malformed. See TestIssue585. - if i >= len(b) { - i-- + n := j - i + if n >= 3 { + if n > 5 { + return nil, nil, nil, NewParserError(b[i:j], "too many quotes at the end of a multiline basic string") } - seenSpace = true - hasTime = true - } else { - break byteLoop + buf = append(buf, b[i:i+n-3]...) + return b[:j], buf, b[j:], nil + } + buf = append(buf, b[i:j]...) + i = j + case c == '\\': + i++ + if i >= len(b) { + return nil, nil, nil, NewParserError(b[i-1:], `need a character after \`) } + // Escaped newline: backslash, optional whitespace, newline, + // then all following whitespace and newlines are trimmed. + if b[i] == ' ' || b[i] == '\t' || b[i] == '\n' || b[i] == '\r' { + j := i + for j < len(b) && (b[j] == ' ' || b[j] == '\t') { + j++ + } + if j < len(b) && b[j] == '\r' { + j++ + } + if j >= len(b) || b[j] != '\n' { + return nil, nil, nil, NewParserError(b[i-1:i+1], "invalid escape character %#U", b[i]) + } + j++ + for j < len(b) && (b[j] == ' ' || b[j] == '\t' || b[j] == '\n' || b[j] == '\r') { + // note: a lone \r not followed by \n will be caught on + // the next iteration of the outer loop. + if b[j] == '\r' { + if j+1 >= len(b) || b[j+1] != '\n' { + break + } + j++ + } + j++ + } + i = j + continue + } + var err error + buf, i, err = unescape(buf, b, i) + if err != nil { + return nil, nil, nil, err + } + case c >= 0x20 && c < 0x7f: + buf = append(buf, c) + i++ + case c == '\t' || c == '\n': + buf = append(buf, c) + i++ + case c == '\r': + if i+1 < len(b) && b[i+1] == '\n' { + buf = append(buf, '\r', '\n') + i += 2 + continue + } + return nil, nil, nil, NewParserError(b[i:i+1], "carriage returns must be followed by a newline character") + case c < 0x80: + return nil, nil, nil, NewParserError(b[i:i+1], "multiline basic strings cannot have control characters") default: - break byteLoop - } - } - - var kind Kind - - if hasTime { - if hasDate { - if hasTz { - kind = DateTime - } else { - kind = LocalDateTime + j, ok := scanUtf8Run(b, i) + if !ok { + return nil, nil, nil, NewParserError(b[i:i+1], "invalid UTF-8 character in multiline basic string") } - } else { - kind = LocalTime + buf = append(buf, b[i:j]...) + i = j } - } else { - kind = LocalDate } - - return p.builder.Push(Node{ - Kind: kind, - Data: b[:i], - }), b[i:], nil + return nil, nil, nil, NewParserError(b[len(b):], `multiline basic string not terminated by """`) } -//nolint:funlen,gocognit,cyclop -func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) { - i := 0 - - if len(b) > 2 && b[0] == '0' && b[1] != '.' && b[1] != 'e' && b[1] != 'E' { - var isValidRune validRuneFn - - switch b[1] { - case 'x': - isValidRune = isValidHexRune - case 'o': - isValidRune = isValidOctalRune - case 'b': - isValidRune = isValidBinaryRune - default: - i++ - } - - if isValidRune != nil { +// scanUtf8Run consumes a run of valid non-ASCII UTF-8 runes starting at +// b[i]. It returns the index of the first byte after the run, and whether +// the run was entirely valid. Processing whole runs amortizes the cost of +// the call compared to validating rune by rune. +func scanUtf8Run(b []byte, i int) (int, bool) { + for i < len(b) { + c := b[i] + switch { + case c < 0x80: + return i, true + case c < 0xC2: + return i, false + case c < 0xE0: + if i+1 >= len(b) || b[i+1]&0xC0 != 0x80 { + return i, false + } i += 2 - for ; i < len(b); i++ { - if !isValidRune(b[i]) { - break + case c < 0xF0: + if i+2 >= len(b) || b[i+2]&0xC0 != 0x80 { + return i, false + } + b1 := b[i+1] + switch c { + case 0xE0: + if b1 < 0xA0 || b1 > 0xBF { + return i, false + } + case 0xED: + // exclude surrogates + if b1 < 0x80 || b1 > 0x9F { + return i, false + } + default: + if b1&0xC0 != 0x80 { + return i, false } } - } - - return p.builder.Push(Node{ - Kind: Integer, - Data: b[:i], - Raw: p.Range(b[:i]), - }), b[i:], nil - } - - isFloat := false - - for ; i < len(b); i++ { - c := b[i] - - if c >= '0' && c <= '9' || c == '+' || c == '-' || c == '_' { - continue - } - - if c == '.' || c == 'e' || c == 'E' { - isFloat = true - - continue - } - - if c == 'i' { - if scanFollowsInf(b[i:]) { - return p.builder.Push(Node{ - Kind: Float, - Data: b[:i+3], - Raw: p.Range(b[:i+3]), - }), b[i+3:], nil + i += 3 + case c < 0xF5: + if i+3 >= len(b) || b[i+2]&0xC0 != 0x80 || b[i+3]&0xC0 != 0x80 { + return i, false } - - return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'i' while scanning for a number") - } - - if c == 'n' { - if scanFollowsNan(b[i:]) { - return p.builder.Push(Node{ - Kind: Float, - Data: b[:i+3], - Raw: p.Range(b[:i+3]), - }), b[i+3:], nil + b1 := b[i+1] + switch c { + case 0xF0: + if b1 < 0x90 || b1 > 0xBF { + return i, false + } + case 0xF4: + if b1 < 0x80 || b1 > 0x8F { + return i, false + } + default: + if b1&0xC0 != 0x80 { + return i, false + } } - - return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'n' while scanning for a number") + i += 4 + default: + return i, false } - - break - } - - if i == 0 { - return invalidReference, b, NewParserError(b, "incomplete number") - } - - kind := Integer - - if isFloat { - kind = Float } - - return p.builder.Push(Node{ - Kind: kind, - Data: b[:i], - Raw: p.Range(b[:i]), - }), b[i:], nil -} - -func isDigit(r byte) bool { - return r >= '0' && r <= '9' -} - -type validRuneFn func(r byte) bool - -func isValidHexRune(r byte) bool { - return r >= 'a' && r <= 'f' || - r >= 'A' && r <= 'F' || - r >= '0' && r <= '9' || - r == '_' -} - -func isValidOctalRune(r byte) bool { - return r >= '0' && r <= '7' || r == '_' -} - -func isValidBinaryRune(r byte) bool { - return r == '0' || r == '1' || r == '_' -} - -func expect(x byte, b []byte) ([]byte, error) { - if len(b) == 0 { - return nil, NewParserError(b, "expected character %c but the document ended here", x) - } - - if b[0] != x { - return nil, NewParserError(b[0:1], "expected character %c", x) - } - - return b[1:], nil + return i, true } diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go b/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go deleted file mode 100644 index 0512181d..00000000 --- a/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go +++ /dev/null @@ -1,270 +0,0 @@ -package unstable - -import "github.com/pelletier/go-toml/v2/internal/characters" - -func scanFollows(b []byte, pattern string) bool { - n := len(pattern) - - return len(b) >= n && string(b[:n]) == pattern -} - -func scanFollowsMultilineBasicStringDelimiter(b []byte) bool { - return scanFollows(b, `"""`) -} - -func scanFollowsMultilineLiteralStringDelimiter(b []byte) bool { - return scanFollows(b, `'''`) -} - -func scanFollowsTrue(b []byte) bool { - return scanFollows(b, `true`) -} - -func scanFollowsFalse(b []byte) bool { - return scanFollows(b, `false`) -} - -func scanFollowsInf(b []byte) bool { - return scanFollows(b, `inf`) -} - -func scanFollowsNan(b []byte) bool { - return scanFollows(b, `nan`) -} - -func scanUnquotedKey(b []byte) ([]byte, []byte) { - // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ - for i := 0; i < len(b); i++ { - if !isUnquotedKeyChar(b[i]) { - return b[:i], b[i:] - } - } - - return b, b[len(b):] -} - -func isUnquotedKeyChar(r byte) bool { - return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' || r == '_' -} - -func scanLiteralString(b []byte) ([]byte, []byte, error) { - // literal-string = apostrophe *literal-char apostrophe - // apostrophe = %x27 ; ' apostrophe - // literal-char = %x09 / %x20-26 / %x28-7E / non-ascii - for i := 1; i < len(b); { - switch b[i] { - case '\'': - return b[:i+1], b[i+1:], nil - case '\n', '\r': - return nil, nil, NewParserError(b[i:i+1], "literal strings cannot have new lines") - } - size := characters.Utf8ValidNext(b[i:]) - if size == 0 { - return nil, nil, NewParserError(b[i:i+1], "invalid character") - } - i += size - } - - return nil, nil, NewParserError(b[len(b):], "unterminated literal string") -} - -func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) { - // ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body - // ml-literal-string-delim - // ml-literal-string-delim = 3apostrophe - // ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ] - // - // mll-content = mll-char / newline - // mll-char = %x09 / %x20-26 / %x28-7E / non-ascii - // mll-quotes = 1*2apostrophe - for i := 3; i < len(b); { - switch b[i] { - case '\'': - if scanFollowsMultilineLiteralStringDelimiter(b[i:]) { - i += 3 - - // At that point we found 3 apostrophe, and i is the - // index of the byte after the third one. The scanner - // needs to be eager, because there can be an extra 2 - // apostrophe that can be accepted at the end of the - // string. - - if i >= len(b) || b[i] != '\'' { - return b[:i], b[i:], nil - } - i++ - - if i >= len(b) || b[i] != '\'' { - return b[:i], b[i:], nil - } - i++ - - if i < len(b) && b[i] == '\'' { - return nil, nil, NewParserError(b[i-3:i+1], "''' not allowed in multiline literal string") - } - - return b[:i], b[i:], nil - } - case '\r': - if len(b) < i+2 { - return nil, nil, NewParserError(b[len(b):], `need a \n after \r`) - } - if b[i+1] != '\n' { - return nil, nil, NewParserError(b[i:i+2], `need a \n after \r`) - } - i += 2 // skip the \n - continue - } - size := characters.Utf8ValidNext(b[i:]) - if size == 0 { - return nil, nil, NewParserError(b[i:i+1], "invalid character") - } - i += size - } - - return nil, nil, NewParserError(b[len(b):], `multiline literal string not terminated by '''`) -} - -func scanWindowsNewline(b []byte) ([]byte, []byte, error) { - const lenCRLF = 2 - if len(b) < lenCRLF { - return nil, nil, NewParserError(b, "windows new line expected") - } - - if b[1] != '\n' { - return nil, nil, NewParserError(b, `windows new line should be \r\n`) - } - - return b[:lenCRLF], b[lenCRLF:], nil -} - -func scanWhitespace(b []byte) ([]byte, []byte) { - for i := 0; i < len(b); i++ { - switch b[i] { - case ' ', '\t': - continue - default: - return b[:i], b[i:] - } - } - - return b, b[len(b):] -} - -func scanComment(b []byte) ([]byte, []byte, error) { - // comment-start-symbol = %x23 ; # - // non-ascii = %x80-D7FF / %xE000-10FFFF - // non-eol = %x09 / %x20-7F / non-ascii - // - // comment = comment-start-symbol *non-eol - - for i := 1; i < len(b); { - if b[i] == '\n' { - return b[:i], b[i:], nil - } - if b[i] == '\r' { - if i+1 < len(b) && b[i+1] == '\n' { - return b[:i+1], b[i+1:], nil - } - return nil, nil, NewParserError(b[i:i+1], "invalid character in comment") - } - size := characters.Utf8ValidNext(b[i:]) - if size == 0 { - return nil, nil, NewParserError(b[i:i+1], "invalid character in comment") - } - - i += size - } - - return b, b[len(b):], nil -} - -func scanBasicString(b []byte) ([]byte, bool, []byte, error) { - // basic-string = quotation-mark *basic-char quotation-mark - // quotation-mark = %x22 ; " - // basic-char = basic-unescaped / escaped - // basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii - // escaped = escape escape-seq-char - escaped := false - i := 1 - - for ; i < len(b); i++ { - switch b[i] { - case '"': - return b[:i+1], escaped, b[i+1:], nil - case '\n', '\r': - return nil, escaped, nil, NewParserError(b[i:i+1], "basic strings cannot have new lines") - case '\\': - if len(b) < i+2 { - return nil, escaped, nil, NewParserError(b[i:i+1], "need a character after \\") - } - escaped = true - i++ // skip the next character - } - } - - return nil, escaped, nil, NewParserError(b[len(b):], `basic string not terminated by "`) -} - -func scanMultilineBasicString(b []byte) ([]byte, bool, []byte, error) { - // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body - // ml-basic-string-delim - // ml-basic-string-delim = 3quotation-mark - // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] - // - // mlb-content = mlb-char / newline / mlb-escaped-nl - // mlb-char = mlb-unescaped / escaped - // mlb-quotes = 1*2quotation-mark - // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii - // mlb-escaped-nl = escape ws newline *( wschar / newline ) - - escaped := false - i := 3 - - for ; i < len(b); i++ { - switch b[i] { - case '"': - if scanFollowsMultilineBasicStringDelimiter(b[i:]) { - i += 3 - - // At that point we found 3 apostrophe, and i is the - // index of the byte after the third one. The scanner - // needs to be eager, because there can be an extra 2 - // apostrophe that can be accepted at the end of the - // string. - - if i >= len(b) || b[i] != '"' { - return b[:i], escaped, b[i:], nil - } - i++ - - if i >= len(b) || b[i] != '"' { - return b[:i], escaped, b[i:], nil - } - i++ - - if i < len(b) && b[i] == '"' { - return nil, escaped, nil, NewParserError(b[i-3:i+1], `""" not allowed in multiline basic string`) - } - - return b[:i], escaped, b[i:], nil - } - case '\\': - if len(b) < i+2 { - return nil, escaped, nil, NewParserError(b[len(b):], "need a character after \\") - } - escaped = true - i++ // skip the next character - case '\r': - if len(b) < i+2 { - return nil, escaped, nil, NewParserError(b[len(b):], `need a \n after \r`) - } - if b[i+1] != '\n' { - return nil, escaped, nil, NewParserError(b[i:i+2], `need a \n after \r`) - } - i++ // skip the \n - } - } - - return nil, escaped, nil, NewParserError(b[len(b):], `multiline basic string not terminated by """`) -} diff --git a/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go b/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go index 00cfd6de..c84c90cc 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go @@ -1,7 +1,43 @@ package unstable -// The Unmarshaler interface may be implemented by types to customize their -// behavior when being unmarshaled from a TOML document. +// Unmarshaler is implemented by types that can unmarshal a TOML description +// of themselves. The input is a valid TOML document containing the relevant +// portion of the parsed document. +// +// For tables (including split tables defined in multiple places), the data +// contains the raw key-value bytes from the original document with adjusted +// table headers to be relative to the unmarshaling target. +// +// When the decoding target itself implements this interface, it receives the +// whole document — every top-level key-value as well as every table and array +// table — assembled into a single valid TOML document and delivered once. type Unmarshaler interface { - UnmarshalTOML(value *Node) error + UnmarshalTOML(data []byte) error +} + +// RawMessage is a raw encoded TOML value. It implements both Unmarshaler and +// Marshaler and can be used to delay TOML decoding or capture raw content, +// similar to json.RawMessage. +// +// Decoding (requires Decoder.EnableUnmarshalerInterface) captures the raw TOML +// bytes for the target without decoding them: +// +// type Config struct { +// Plugin RawMessage `toml:"plugin"` +// } +// +// var cfg Config +// toml.NewDecoder(r).EnableUnmarshalerInterface().Decode(&cfg) +// // cfg.Plugin now contains the raw TOML bytes for [plugin] +// +// Encoding (requires Encoder.EnableMarshalerInterface) splices the stored bytes +// back into the document verbatim: +// +// toml.NewEncoder(w).EnableMarshalerInterface().Encode(cfg) +type RawMessage []byte + +// UnmarshalTOML implements Unmarshaler. +func (m *RawMessage) UnmarshalTOML(data []byte) error { + *m = append((*m)[:0], data...) + return nil } diff --git a/vendor/github.com/raeperd/recvcheck/.golangci.yml b/vendor/github.com/raeperd/recvcheck/.golangci.yml index 18692d50..3ef4d52f 100644 --- a/vendor/github.com/raeperd/recvcheck/.golangci.yml +++ b/vendor/github.com/raeperd/recvcheck/.golangci.yml @@ -1,10 +1,14 @@ +version: "2" + +formatters: + enable: + - gofumpt + - gci + linters: enable: - recvcheck - -output: - show-stats: true - sort-results: true - sort-order: - - linter - - file + exclusions: + presets: + - common-false-positives + - std-error-handling diff --git a/vendor/github.com/raeperd/recvcheck/README.md b/vendor/github.com/raeperd/recvcheck/README.md index 067aa3c5..e51acda8 100644 --- a/vendor/github.com/raeperd/recvcheck/README.md +++ b/vendor/github.com/raeperd/recvcheck/README.md @@ -1,52 +1,136 @@ # recvcheck -[![.github/workflows/build.yaml](https://github.com/raeperd/recvcheck/actions/workflows/build.yaml/badge.svg)](https://github.com/raeperd/recvcheck/actions/workflows/build.yaml) [![Go Report Card](https://goreportcard.com/badge/github.com/raeperd/recvcheck)](https://goreportcard.com/report/github.com/raeperd/recvcheck) -Golang linter for check receiver type in method -## Motivation -From [Go Wiki: Go Code Review Comments - The Go Programming Language](https://go.dev/wiki/CodeReviewComments#receiver-type) -> Don’t mix receiver types. Choose either pointers or struct types for all available method +[![Build Status](https://github.com/raeperd/recvcheck/actions/workflows/build.yaml/badge.svg)](https://github.com/raeperd/recvcheck/actions/workflows/build.yaml) +[![Go Report Card](https://goreportcard.com/badge/github.com/raeperd/recvcheck)](https://goreportcard.com/report/github.com/raeperd/recvcheck) +[![Coverage Status](https://coveralls.io/repos/github/raeperd/recvcheck/badge.svg?branch=main)](https://coveralls.io/github/raeperd/recvcheck?branch=main) +[![Go Reference](https://pkg.go.dev/badge/github.com/raeperd/recvcheck.svg)](https://pkg.go.dev/github.com/raeperd/recvcheck) -Following code from [Dave Cheney](https://dave.cheney.net/2015/11/18/wednesday-pop-quiz-spot-the-race) causes data race. Could you find it? -This linter does it for you. +Go linter that detects mixing pointer and value method receivers. -```go -package main +## Why + +Mixing pointer and value receivers on the same type creates **subtle, hard-to-detect bugs** that can cause: -import ( - "fmt" - "time" -) +### 🐛 Data Races +When you copy a struct with a mutex, you copy the mutex state, leading to race conditions: +```go type RPC struct { - result int - done chan struct{} + mu sync.Mutex // This is the problem! + result int + done chan struct{} } func (rpc *RPC) compute() { - time.Sleep(time.Second) // strenuous computation intensifies - rpc.result = 42 - close(rpc.done) + rpc.mu.Lock() + defer rpc.mu.Unlock() + rpc.result = 42 } -func (RPC) version() int { - return 1 // never going to need to change this +func (RPC) version() int { // Value receiver copies the mutex! + return 1 } func main() { - rpc := &RPC{done: make(chan struct{})} + rpc := &RPC{done: make(chan struct{})} + go rpc.compute() // Locks original mutex + version := rpc.version() // Uses copied mutex - RACE! + // ... +} +``` - go rpc.compute() // kick off computation in the background - version := rpc.version() // grab some other information while we're waiting - <-rpc.done // wait for computation to finish - result := rpc.result +### 🚨 Silent Bugs - fmt.Printf("RPC computation complete, result: %d, version: %d\n", result, version) +Value receivers create copies, so modifications are lost: + +```go +type Counter struct { + value int } + +func (c *Counter) Increment() { c.value++ } // pointer receiver +func (c Counter) Reset() { c.value = 0 } // value receiver - NO EFFECT! +``` + +### 🤔 Developer Confusion + +Mixed receivers make code behavior unpredictable and harder to reason about. + +### ✅ The Solution + +**Consistency is key**: Go's official guidance says [Don't mix receiver types](https://go.dev/wiki/CodeReviewComments#receiver-type). Choose either pointers or values for **all** methods on a type. + +`recvcheck` automatically detects these issues before they reach production. + +## Installation + +```bash +# Standalone +go install github.com/raeperd/recvcheck/cmd/recvcheck@latest + +# With golangci-lint (recommended) +# Add to .golangci.yml: +linters: + enable: + - recvcheck +``` + +## Usage + +```bash +recvcheck ./... +# or +golangci-lint run +``` + +Output: +``` +main.go:8:1: the methods of "RPC" use pointer receiver and non-pointer receiver +``` + +## Configuration + +```yaml +# .golangci.yml +linters-settings: + recvcheck: + # Disable default exclusions (MarshalJSON, etc.) + disable-builtin: false + + # Custom exclusions + exclusions: + - "Server.Shutdown" # Specific method + - "*.String" # All String methods +``` + +### Default Exclusions + +Unmarshal methods are excluded by default as they must use pointer receivers: +- `*.UnmarshalText`, `*.UnmarshalJSON`, `*.UnmarshalYAML` +- `*.UnmarshalXML`, `*.UnmarshalBinary`, `*.GobDecode` + +## Examples + +❌ **Bad** - Mixed receivers: +```go +func (u *User) SetName(name string) { } // pointer +func (u User) GetName() string { } // value - inconsistent! ``` -## References -- [Is there a way to detect following data race code using golangci-lint or other linter?? · golangci/golangci-lint · Discussion #5006](https://github.com/golangci/golangci-lint/discussions/5006) - - [Wednesday pop quiz: spot the race | Dave Cheney](https://dave.cheney.net/2015/11/18/wednesday-pop-quiz-spot-the-race) +✅ **Good** - Consistent receivers: +```go +func (u *User) SetName(name string) { } // pointer +func (u *User) GetName() string { } // pointer - consistent! +``` +## Contributing + +```bash +make test # Run tests +make lint # Run linter +make build # Build binary +``` +## License +MIT \ No newline at end of file diff --git a/vendor/github.com/raeperd/recvcheck/analyzer.go b/vendor/github.com/raeperd/recvcheck/analyzer.go index 11fb38e7..a67d72e8 100644 --- a/vendor/github.com/raeperd/recvcheck/analyzer.go +++ b/vendor/github.com/raeperd/recvcheck/analyzer.go @@ -15,14 +15,14 @@ func NewAnalyzer(s Settings) *analysis.Analyzer { } if !s.DisableBuiltin { - // Default excludes for Marshal/Encode methods https://github.com/raeperd/recvcheck/issues/7 + // Default excludes for Unmarshal/Decode methods https://github.com/raeperd/recvcheck/issues/17 a.excluded = map[string]struct{}{ - "*.MarshalText": {}, - "*.MarshalJSON": {}, - "*.MarshalYAML": {}, - "*.MarshalXML": {}, - "*.MarshalBinary": {}, - "*.GobEncode": {}, + "*.UnmarshalText": {}, + "*.UnmarshalJSON": {}, + "*.UnmarshalYAML": {}, + "*.UnmarshalXML": {}, + "*.UnmarshalBinary": {}, + "*.GobDecode": {}, } } @@ -42,12 +42,12 @@ func NewAnalyzer(s Settings) *analysis.Analyzer { type Settings struct { // DisableBuiltin if true, disables the built-in method excludes. // Built-in excluded methods: - // - "MarshalText" - // - "MarshalJSON" - // - "MarshalYAML" - // - "MarshalXML" - // - "MarshalBinary" - // - "GobEncode" + // - "UnmarshalText" + // - "UnmarshalJSON" + // - "UnmarshalYAML" + // - "UnmarshalXML" + // - "UnmarshalBinary" + // - "GobDecode" DisableBuiltin bool // Exclusions format is `struct_name.method_name` (ex: `Foo.MethodName`). diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/.dockerignore b/vendor/github.com/ryancurrah/gomodguard/v2/.dockerignore new file mode 100644 index 00000000..77738287 --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/.dockerignore @@ -0,0 +1 @@ +dist/ \ No newline at end of file diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/.gitignore b/vendor/github.com/ryancurrah/gomodguard/v2/.gitignore new file mode 100644 index 00000000..4ebc79c5 --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/.gitignore @@ -0,0 +1,25 @@ +# 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/ + +/gomodguard + +*.xml + +dist/ + +coverage.* + +.idea/ diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/.golangci.yml b/vendor/github.com/ryancurrah/gomodguard/v2/.golangci.yml new file mode 100644 index 00000000..2e3266ad --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/.golangci.yml @@ -0,0 +1,35 @@ +version: "2" + +linters: + default: all + disable: + - lll + - gomodguard + - gomoddirectives + - gochecknoglobals + - paralleltest + - varnamelen + - exhaustruct + - depguard + - forbidigo + - funlen + - nlreturn + - nonamedreturns + - cyclop + - err113 + - perfsprint + - tagliatelle + - wrapcheck + - mnd + - wsl + - noinlineerr + settings: + revive: + rules: + - name: package-comments + disabled: true + exclusions: + rules: + - path: _test\.go$ + linters: + - goconst diff --git a/vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE b/vendor/github.com/ryancurrah/gomodguard/v2/LICENSE similarity index 96% rename from vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE rename to vendor/github.com/ryancurrah/gomodguard/v2/LICENSE index 25cec1ed..acd8a81e 100644 --- a/vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE +++ b/vendor/github.com/ryancurrah/gomodguard/v2/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Ayman Bagabas +Copyright (c) 2020 Ryan Currah 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/ryancurrah/gomodguard/v2/Makefile b/vendor/github.com/ryancurrah/gomodguard/v2/Makefile new file mode 100644 index 00000000..d30a2255 --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/Makefile @@ -0,0 +1,87 @@ +current_dir = $(shell pwd) + +.PHONY: goimports +goimports: + find . -name '*.go' -exec goimports -w -local github.com/ryancurrah/gomodguard {} + + +.PHONY: lint +lint: + golangci-lint run ./... + cd cmd/gomodguard && golangci-lint run ./... + +.PHONY: tidy +tidy: + go mod tidy + cd cmd/gomodguard && go mod tidy + +.PHONY: build +build: + cd cmd/gomodguard && go build -o "$$(go env GOPATH)/bin/gomodguard" main.go + +.PHONY: run +run: build + ./gomodguard + +.PHONY: test +test: + go test -v -coverprofile coverage.out + cd cmd/gomodguard && go test -v -coverprofile coverage.out ./... + cat cmd/gomodguard/coverage.out | tail -n +2 >> coverage.out + +.PHONY: cover +cover: + gocover-cobertura < coverage.out > coverage.xml + +.PHONY: dockerrun +dockerrun: dockerbuild + docker run -v "${current_dir}/.gomodguard.yaml:/.gomodguard.yaml" ryancurrah/gomodguard:latest + +.PHONY: snapshot +snapshot: + cd cmd/gomodguard && goreleaser --clean --snapshot + +.PHONY: release +release: + cd cmd/gomodguard && goreleaser --clean + +.PHONY: clean +clean: + rm -rf dist/ + rm -f gomodguard coverage.xml coverage.out + rm -f cmd/gomodguard/coverage.out + +.PHONY: tag +tag: + @if [ -n "$$(git status --porcelain)" ]; then \ + echo "error: working tree not clean"; exit 1; \ + fi; \ + current=$$(git tag --sort=-v:refname --list 'v*' | head -n1 || echo "none"); \ + read -p "Current version: $$current. Enter new version: " version; \ + if [ -z "$$version" ]; then echo "error: version required"; exit 1; fi; \ + bump_branch="bump-library-to-$$version"; \ + git tag "$$version" && \ + git push origin "$$version" && \ + git checkout -b "$$bump_branch" && \ + (cd cmd/gomodguard && GOWORK=off go get "github.com/ryancurrah/gomodguard/v2@$$version" && GOWORK=off go mod tidy) && \ + git add cmd/gomodguard/go.mod cmd/gomodguard/go.sum && \ + git commit -m "chore: bump library to $$version" && \ + git tag "cmd/gomodguard/$$version" && \ + git push -u origin "$$bump_branch" "cmd/gomodguard/$$version" && \ + gh pr create --title "chore: bump library to $$version" --body "Required by cmd/gomodguard/$$version release." && \ + echo "waiting for PR to merge..." && \ + while :; do \ + state=$$(gh pr view --json state -q .state); \ + if [ "$$state" = "MERGED" ]; then break; fi; \ + if [ "$$state" = "CLOSED" ]; then echo "error: PR closed without merge"; exit 1; fi; \ + sleep 30; \ + done && \ + git checkout main && git pull --ff-only origin main && \ + git branch -D "$$bump_branch" + +.PHONY: install-mac-tools +install-tools-mac: + brew install goreleaser/tap/goreleaser + +.PHONY: install-go-tools +install-go-tools: + go install -v github.com/t-yuki/gocover-cobertura@latest diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/README.md b/vendor/github.com/ryancurrah/gomodguard/v2/README.md new file mode 100644 index 00000000..a0bea99f --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/README.md @@ -0,0 +1,219 @@ +# gomodguard +[![License](https://img.shields.io/github/license/ryancurrah/gomodguard?style=flat-square)](/LICENSE) +[![Codecov](https://img.shields.io/codecov/c/gh/ryancurrah/gomodguard?style=flat-square)](https://codecov.io/gh/ryancurrah/gomodguard) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/ryancurrah/gomodguard/go.yml?branch=main&logo=Go&style=flat-square)](https://github.com/ryancurrah/gomodguard/actions?query=workflow%3AGo) +[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/ryancurrah/gomodguard?style=flat-square)](https://github.com/ryancurrah/gomodguard/releases/latest) +[![Docker](https://img.shields.io/docker/pulls/ryancurrah/gomodguard?style=flat-square)](https://hub.docker.com/r/ryancurrah/gomodguard) +[![Github Releases Stats of golangci-lint](https://img.shields.io/github/downloads/ryancurrah/gomodguard/total.svg?logo=github&style=flat-square)](https://somsubhra.com/github-release-stats/?username=ryancurrah&repository=gomodguard) + + + +Allow and block list linter for direct Go module dependencies. This is useful for organizations where they want to standardize on the modules used and be able to recommend alternative modules. + +## Description + +Allowed and blocked modules are defined in a `./.gomodguard.yaml` or `~/.gomodguard.yaml` file. + +Modules can be allowed by module or prefix name. When allowed modules are specified any modules not in the allowed configuration are blocked. + +If no allowed modules or module prefixes are specified then all modules are allowed except for blocked ones. + +The linter looks for blocked modules in `go.mod` and searches for imported packages where the imported packages module is blocked. Indirect modules are not considered. + +Alternative modules can be optionally recommended in the blocked modules list. + +If the linted module imports a blocked module but the linted module is in the recommended modules list the blocked module is ignored. Usually, this means the linted module wraps that blocked module for use by other modules, therefore the import of the blocked module should not be blocked. + +Version constraints can be specified for modules as well which lets you block new or old versions of modules or specific versions. + +Results are printed to `stdout`. + +Logging statements are printed to `stderr`. + +Results can be exported to different report formats. Which can be imported into CI tools. See the help section for more information. + +# Configuration + +```yaml +# allowed defines the modules that are permitted as direct dependencies. +# When this section is non-empty, any module not matched by an entry is blocked. +# When omitted entirely, all modules are allowed except those in the blocked list. +allowed: + # Exact match (default when match-type is omitted). + - module: go.yaml.in/yaml/v4 + - module: github.com/go-xmlfmt/xmlfmt + + # version constrains which versions of the module are allowed. + # Uses semver constraint syntax (e.g. ">= 1.0.0", "~1.2", "== 2.5.0"). + - module: github.com/confluentinc/confluent-kafka-go/v2 + version: "== 2.5.0" + + # match-type controls how the module is matched against module paths. + # Options: exact (default), prefix, regex + - module: github.com/kubernetes + match-type: prefix + - module: github.com/apache/arrow-go + match-type: prefix + - module: "github.com/somecompany/.*" + match-type: regex + +# blocked defines modules that are not permitted as direct dependencies. +blocked: + - module: github.com/uudashr/go-module + # match-type controls how the module is matched against module paths. + # Options: exact (default), prefix, regex + match-type: exact + + # recommendations lists alternative modules to suggest in the lint error. + recommendations: + - golang.org/x/mod + + # reason is a human-readable explanation appended to the lint error. + reason: "`mod` is the official go.mod parser library." + + - module: github.com/mitchellh/go-homedir + # version constrains which versions of the module are blocked. + # Uses semver constraint syntax. When omitted, all versions are blocked. + version: "<= 1.1.0" + reason: "old versions have a known bug." + + - module: "github.com/badcompany/.*" + match-type: regex + reason: "No badcompany packages are permitted." + +# Blocks 'replace' directives using local filesystem paths to prevent +# accidental commits of dev overrides. Sibling modules in multi-module +# repos are automatically detected and permitted. +local_replace_directives: true +``` + +### Field reference + +#### Top-level fields + +| Field | Type | Default | Description | +|---|---|---|---| +| `allowed` | list | *(none)* | Modules that are permitted. When non-empty, anything not matched is blocked. | +| `blocked` | list | *(none)* | Modules that are explicitly blocked. | +| `local_replace_directives` | bool | `false` | Block any module whose `replace` directive points to a local filesystem path. Multi-module repo aware: sibling modules whose replacement path contains a matching `go.mod` are not blocked. | + +#### `allowed` / `blocked` entry fields + +| Field | Type | Description | +|---|---|---| +| `module` | string | The module path to match against. | +| `match-type` | `exact` \| `prefix` \| `regex` | How `module` is matched against dependency paths. Defaults to `exact`. | +| `version` | semver constraint string | Restricts the rule to specific versions (e.g. `<= 1.2.0`, `>= 2.0.0`). When omitted, all versions match. | +| `recommendations` | list of module paths | *(blocked only)* Alternative modules to suggest in the lint error. If the module being linted is itself in this list, the block is skipped. | +| `reason` | string | *(blocked only)* Human-readable explanation appended to the lint error. | + +#### Match type precedence + +When multiple rules can match the same module the following precedence applies: + +1. **Exact match** — highest priority; wins over prefix and regex. +2. **Prefix match** — next priority; longest matching prefix wins. +3. **Regex match** — lowest priority; evaluated in alphabetical key order; first match wins. + +## Example .gomodguard.yaml Files + +The following example configuration files are available: + +- [examples/alloptions/.gomodguard.yaml](examples/alloptions/.gomodguard.yaml) +- [examples/allowedversion/.gomodguard.yaml](examples/allowedversion/.gomodguard.yaml) +- [examples/emptyallowlist/.gomodguard.yaml](examples/emptyallowlist/.gomodguard.yaml) +- [examples/indirectdep/.gomodguard.yaml](examples/indirectdep/.gomodguard.yaml) +- [examples/majorversion/.gomodguard.yaml](examples/majorversion/.gomodguard.yaml) +- [examples/regexversion/.gomodguard.yaml](examples/regexversion/.gomodguard.yaml) +- [examples/regextest/.gomodguard.yaml](examples/regextest/.gomodguard.yaml) + +### Migrating from v1 + +If you have a v1 `.gomodguard.yaml` file, you can automatically migrate it to the new v2 schema by running: + +``` +gomodguard migrate > .gomodguard-v2.yaml +mv .gomodguard-v2.yaml .gomodguard.yaml +``` + +## Usage + +``` +╰─ gomodguard -help +Usage: gomodguard [files...] +Also supports package syntax but will use it in relative path, i.e. ./pkg/... + +Commands: + (default) Lint Go module dependencies using the configuration file + migrate Convert a v1 .gomodguard.yaml config file to v2 format and print to stdout + +Flags: + -f string + Report results to the specified file. A report type must also be specified + -file string + + -h Show this help text + -help + + -i int + Exit code when issues were found (default 2) + -issues-exit-code int + (default 2) + -n Don't lint test files + -no-test + + -r string + Report results to one of the following formats: checkstyle. A report file destination must also be specified + -report string + + -version + Print the version +``` + +## Example + +``` +╰─ cd examples/alloptions +╰─ gomodguard -r checkstyle -f gomodguard-checkstyle.xml ./... + +info: allowed modules, [github.com/Masterminds/semver/v3 github.com/go-xmlfmt/xmlfmt golang.org gopkg.in/yaml.v3] +info: blocked modules, [github.com/gofrs/uuid github.com/mitchellh/go-homedir github.com/uudashr/go-module] +blocked_example.go:6:1 import of package `github.com/gofrs/uuid` is blocked because the module is in the blocked modules list. `github.com/ryancurrah/gomodguard` is a recommended module. testing if module is not blocked when it is recommended. +blocked_example.go:7:1 import of package `github.com/mitchellh/go-homedir` is blocked because the module is in the blocked modules list. version `v1.1.0` is blocked because it does not meet the version constraint `<=1.1.0`. testing if blocked version constraint works. +blocked_example.go:8:1 import of package `github.com/uudashr/go-module` is blocked because the module is in the blocked modules list. `golang.org/x/mod` is a recommended module. `mod` is the official go.mod parser library. +``` + +Resulting checkstyle file + +``` +╰─ cat gomodguard-checkstyle.xml + + + + + + + + + +``` + +## Install + +``` +go install github.com/ryancurrah/gomodguard/cmd/gomodguard/v2@latest +``` + +## Develop + +``` +git clone https://github.com/ryancurrah/gomodguard.git && cd gomodguard/cmd/gomodguard + +go build -o gomodguard main.go +``` + +The repository is a multi-module workspace: the library lives at the root and the CLI lives under `cmd/gomodguard`. A `go.work` file at the repo root wires them together so local CLI builds pick up local library changes without needing a `replace` directive in `cmd/gomodguard/go.mod`. + +## License + +**MIT** diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/allowed.go b/vendor/github.com/ryancurrah/gomodguard/v2/allowed.go new file mode 100644 index 00000000..536a74cb --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/allowed.go @@ -0,0 +1,42 @@ +package gomodguard + +import ( + "fmt" + + "github.com/Masterminds/semver/v3" +) + +// Allowed is a list of modules that are allowed to be used. +type Allowed []AllowedModule + +// AllowedModule is a single entry in the allowed list. +type AllowedModule struct { + Module string `yaml:"module"` + MatchType MatchType `yaml:"match-type"` + Version *semver.Constraints `yaml:"version"` + Matcher Matcher `yaml:"-"` +} + +// CheckVersion returns true if the module version matches the allowed constraint, +// or if no version constraint is specified. +func (r *AllowedModule) CheckVersion(moduleVersion string) (bool, error) { + if r.Version == nil { + return true, nil + } + + version, err := semver.NewVersion(moduleVersion) + if err != nil { + return false, err + } + + return r.Version.Check(version), nil +} + +// NotAllowedReason returns the reason why the module version is not allowed. +func (r *AllowedModule) NotAllowedReason(moduleVersion string) string { + if r == nil || r.Version == nil { + return "the module is not in the allowed modules list." + } + + return fmt.Sprintf("version `%s` does not meet the allowed version constraint `%s`.", moduleVersion, r.Version) +} diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/blocked.go b/vendor/github.com/ryancurrah/gomodguard/v2/blocked.go new file mode 100644 index 00000000..5ba7ee1f --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/blocked.go @@ -0,0 +1,99 @@ +package gomodguard + +import ( + "fmt" + "strings" + + "github.com/Masterminds/semver/v3" +) + +// Blocked is a list of modules that are blocked and not to be used. +type Blocked []BlockedModule + +// BlockedModule is a single entry in the blocked list. +type BlockedModule struct { + Module string `yaml:"module"` + MatchType MatchType `yaml:"match-type"` + Recommendations []string `yaml:"recommendations"` + Reason string `yaml:"reason"` + Version *semver.Constraints `yaml:"version"` + Matcher Matcher `yaml:"-"` +} + +// CheckVersion returns true if the module version matches the blocked constraint. +// If no version constraint is specified, all versions are considered blocked. +func (r *BlockedModule) CheckVersion(moduleVersion string) (bool, error) { + if r.Version == nil { + return true, nil + } + + version, err := semver.NewVersion(moduleVersion) + if err != nil { + return true, err + } + + return r.Version.Check(version), nil +} + +// BlockReason returns the reason why the module or version is blocked. +func (r *BlockedModule) BlockReason(currentModuleVersion string) string { + var sb strings.Builder + + if r.Version != nil { + _, _ = fmt.Fprintf(&sb, "version `%s` is blocked because it does not meet the version constraint `%s`.", + currentModuleVersion, r.Version) + } + + if len(r.Recommendations) > 0 { + if sb.Len() > 0 { + sb.WriteString(" ") + } + + for i := range r.Recommendations { + switch { + case len(r.Recommendations) == 1: + _, _ = fmt.Fprintf(&sb, "`%s` is a recommended module.", r.Recommendations[i]) + case (i+1) != len(r.Recommendations) && (i+1) == (len(r.Recommendations)-1): + _, _ = fmt.Fprintf(&sb, "`%s` ", r.Recommendations[i]) + case (i + 1) != len(r.Recommendations): + _, _ = fmt.Fprintf(&sb, "`%s`, ", r.Recommendations[i]) + default: + _, _ = fmt.Fprintf(&sb, "and `%s` are recommended modules.", r.Recommendations[i]) + } + } + } + + if r.Reason != "" { + if sb.Len() > 0 { + _, _ = fmt.Fprintf(&sb, " %s.", strings.TrimRight(r.Reason, ".")) + } else { + _, _ = fmt.Fprintf(&sb, "%s.", strings.TrimRight(r.Reason, ".")) + } + } + + return sb.String() +} + +// IsCurrentModuleARecommendation returns true if the current module is in the Recommendations list. +func (r *BlockedModule) IsCurrentModuleARecommendation(currentModuleName string) bool { + if r == nil { + return false + } + + for n := range r.Recommendations { + if strings.TrimSpace(currentModuleName) == strings.TrimSpace(r.Recommendations[n]) { + return true + } + } + + return false +} + +// HasRecommendations returns true if the blocked package has recommended modules. +func (r *BlockedModule) HasRecommendations() bool { + if r == nil { + return false + } + + return len(r.Recommendations) > 0 +} diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/codecov.yml b/vendor/github.com/ryancurrah/gomodguard/v2/codecov.yml new file mode 100644 index 00000000..4c475c7c --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/codecov.yml @@ -0,0 +1,9 @@ +coverage: + status: + project: + default: + target: auto + threshold: 5% + patch: + default: + target: auto diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/filesearch.go b/vendor/github.com/ryancurrah/gomodguard/v2/filesearch.go new file mode 100644 index 00000000..4c4769d7 --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/filesearch.go @@ -0,0 +1,66 @@ +package gomodguard + +import ( + "os" + "path/filepath" + "strings" +) + +// Find returns files based on search string arguments and filters. +func Find(cwd string, skipTests bool, args []string) []string { + var ( + foundFiles = []string{} + filteredFiles = []string{} + ) + + for _, f := range args { + if strings.HasSuffix(f, "/...") { + dir, _ := filepath.Split(f) + + foundFiles = append(foundFiles, expandGoWildcard(dir)...) + + continue + } + + if _, err := os.Stat(f); err == nil { + foundFiles = append(foundFiles, f) + } + } + + // Use relative path to print shorter names, sort out test foundFiles if chosen. + for _, f := range foundFiles { + if skipTests { + if strings.HasSuffix(f, "_test.go") { + continue + } + } + + if relativePath, err := filepath.Rel(cwd, f); err == nil { + filteredFiles = append(filteredFiles, relativePath) + + continue + } + + filteredFiles = append(filteredFiles, f) + } + + return filteredFiles +} + +// expandGoWildcard path provided. +func expandGoWildcard(root string) []string { + foundFiles := []string{} + + _ = filepath.Walk(root, func(path string, info os.FileInfo, _ error) error { + // Only append go foundFiles. + if !strings.HasSuffix(info.Name(), ".go") { + return nil + } + + foundFiles = append(foundFiles, path) + + return nil + }) + + return foundFiles +} diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/go.work b/vendor/github.com/ryancurrah/gomodguard/v2/go.work new file mode 100644 index 00000000..63d63678 --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/go.work @@ -0,0 +1,6 @@ +go 1.25.0 + +use ( + . + ./cmd/gomodguard +) diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/go.work.sum b/vendor/github.com/ryancurrah/gomodguard/v2/go.work.sum new file mode 100644 index 00000000..fc90f0f3 --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/go.work.sum @@ -0,0 +1,8 @@ +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/issue.go b/vendor/github.com/ryancurrah/gomodguard/v2/issue.go new file mode 100644 index 00000000..d60fc3a8 --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/issue.go @@ -0,0 +1,20 @@ +package gomodguard + +import ( + "fmt" + "go/token" +) + +// Issue represents the result of one error. +type Issue struct { + FileName string + LineNumber int + Position token.Position + Reason string +} + +// String returns the filename, line +// number and reason of a Issue. +func (r *Issue) String() string { + return fmt.Sprintf("%s:%d:1 %s", r.FileName, r.LineNumber, r.Reason) +} diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/matchers.go b/vendor/github.com/ryancurrah/gomodguard/v2/matchers.go new file mode 100644 index 00000000..ae74b978 --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/matchers.go @@ -0,0 +1,77 @@ +package gomodguard + +import ( + "fmt" + "regexp" + "strings" +) + +// MatchType represents the type of matching to be performed for a module name. +type MatchType string + +const ( + // ExactMatch matches a module name exactly. + ExactMatch MatchType = "exact" + // PrefixMatch matches a module name by prefix. + PrefixMatch MatchType = "prefix" + // RegexMatch matches a module name by regex. + RegexMatch MatchType = "regex" +) + +// Matcher interface for matching module names. +type Matcher interface { + Match(moduleName string) bool +} + +// ExactMatcher matches a module name exactly. +type ExactMatcher struct { + Target string +} + +func (m ExactMatcher) Match(moduleName string) bool { + return strings.TrimSpace(moduleName) == m.Target +} + +// PrefixMatcher matches a module name by prefix. +type PrefixMatcher struct { + Prefix string +} + +// Match returns true if the moduleName starts with the Prefix, ignoring leading/trailing whitespace and case. +func (m PrefixMatcher) Match(moduleName string) bool { + return strings.HasPrefix(strings.TrimSpace(strings.ToLower(moduleName)), strings.ToLower(m.Prefix)) +} + +// RegexMatcher matches a module name by regex. +type RegexMatcher struct { + Regex *regexp.Regexp +} + +func (m RegexMatcher) Match(moduleName string) bool { + if m.Regex == nil { + return false + } + + return m.Regex.MatchString(strings.TrimSpace(moduleName)) +} + +// compileMatcher creates a Matcher based on the match type and pattern. +// +//nolint:ireturn // This factory intentionally returns the Matcher interface. +func compileMatcher(matchType MatchType, pattern string) (Matcher, error) { + switch matchType { + case PrefixMatch: + return PrefixMatcher{Prefix: strings.TrimSpace(pattern)}, nil + case RegexMatch: + re, err := regexp.Compile(strings.TrimSpace(pattern)) + if err != nil { + return nil, err + } + + return RegexMatcher{Regex: re}, nil + case ExactMatch, "": + return ExactMatcher{Target: strings.TrimSpace(pattern)}, nil + default: + return nil, fmt.Errorf("unknown match-type %q for pattern %q", matchType, pattern) + } +} diff --git a/vendor/github.com/ryancurrah/gomodguard/v2/processor.go b/vendor/github.com/ryancurrah/gomodguard/v2/processor.go new file mode 100644 index 00000000..82fa4c2d --- /dev/null +++ b/vendor/github.com/ryancurrah/gomodguard/v2/processor.go @@ -0,0 +1,517 @@ +package gomodguard + +import ( + "bytes" + "cmp" + "encoding/json" + "errors" + "fmt" + "go/parser" + "go/token" + "os" + "os/exec" + "path/filepath" + "regexp" + "slices" + "strings" + + "golang.org/x/mod/modfile" +) + +const ( + goModFilename = "go.mod" + errReadingGoModFile = "unable to read module file %s: %w" + errParsingGoModFile = "unable to parse module file %s: %w" +) + +var ( + blockReasonInBlockedList = "import of package `%s` is blocked because the module is in the blocked modules list." + blockReasonHasLocalReplaceDirective = "import of package `%s` is blocked because the module has a local replace directive." + + // startsWithVersion is used to test when a string begins with the version identifier of a module, + // after having stripped the prefix base module name. IE "github.com/foo/bar/v2/baz" => "v2/baz" + // probably indicates that the module is actually github.com/foo/bar/v2, not github.com/foo/bar. + startsWithVersion = regexp.MustCompile(`^v[0-9]+`) +) + +// ruleIndex provides deterministic, specificity-based rule matching. +// Rules are evaluated in three tiers: +// 1. Exact match — O(1) map lookup. +// 2. Prefix match — longest matching prefix wins. +// 3. Regex match — evaluated in alphabetical key order; first match wins. +type ruleIndex struct { + exactLookup map[string]string // trimmed module name -> original map key + prefixKeys []string // sorted by length desc, then alphabetically + regexKeys []string // sorted alphabetically + matchers map[string]Matcher // key -> compiled matcher +} + +// newRuleIndex categorises rule keys into exact, prefix, and regex tiers +// and pre-sorts the prefix and regex tiers for deterministic evaluation. +func newRuleIndex(keys []string, matchTypes map[string]MatchType, matchers map[string]Matcher) *ruleIndex { + idx := &ruleIndex{ + exactLookup: make(map[string]string, len(keys)), + matchers: matchers, + } + + for _, k := range keys { + switch matchTypes[k] { + case ExactMatch: + idx.exactLookup[strings.TrimSpace(k)] = k + case PrefixMatch: + idx.prefixKeys = append(idx.prefixKeys, k) + case RegexMatch: + idx.regexKeys = append(idx.regexKeys, k) + default: + idx.exactLookup[strings.TrimSpace(k)] = k + } + } + + // Longest prefix first for most-specific match. + slices.SortFunc(idx.prefixKeys, func(a, b string) int { + return cmp.Compare(len(b), len(a)) + }) + + // Alphabetical order for deterministic regex evaluation. + slices.Sort(idx.regexKeys) + + return idx +} + +// bestMatch returns the key of the best-matching rule for moduleName, +// following the tiered precedence: exact > longest prefix > first regex. +func (idx *ruleIndex) bestMatch(moduleName string) (string, bool) { + trimmed := strings.TrimSpace(moduleName) + + // Tier 1: exact match (O(1)) + if key, ok := idx.exactLookup[trimmed]; ok { + return key, true + } + + // Tier 2: longest prefix match + for _, key := range idx.prefixKeys { + if idx.matchers[key].Match(moduleName) { + return key, true + } + } + + // Tier 3: first regex match (alphabetical order) + for _, key := range idx.regexKeys { + if idx.matchers[key].Match(moduleName) { + return key, true + } + } + + return "", false +} + +// Configuration of gomodguard allow and block lists. +type Configuration struct { + Allowed Allowed `yaml:"allowed"` + Blocked Blocked `yaml:"blocked"` + LocalReplaceDirectives bool `yaml:"local_replace_directives"` +} + +// InitMatchers initializes matchers for the configuration rules. +func (c *Configuration) InitMatchers() error { + for i := range c.Allowed { + m, err := compileMatcher(c.Allowed[i].MatchType, c.Allowed[i].Module) + if err != nil { + return fmt.Errorf("failed compiling allowed matcher for '%s': %w", c.Allowed[i].Module, err) + } + + c.Allowed[i].Matcher = m + } + + for i := range c.Blocked { + m, err := compileMatcher(c.Blocked[i].MatchType, c.Blocked[i].Module) + if err != nil { + return fmt.Errorf("failed compiling blocked matcher for '%s': %w", c.Blocked[i].Module, err) + } + + c.Blocked[i].Matcher = m + } + + return nil +} + +// Processor processes Go files. +type Processor struct { + Config *Configuration + Modfile *modfile.File + blockedModulesFromModFile map[string][]string +} + +// NewProcessor will create a Processor to lint blocked packages. +func NewProcessor(config *Configuration) (*Processor, error) { + goModFileBytes, err := loadGoModFile() + if err != nil { + return nil, fmt.Errorf(errReadingGoModFile, goModFilename, err) + } + + modFile, err := modfile.Parse(goModFilename, goModFileBytes, nil) + if err != nil { + return nil, fmt.Errorf(errParsingGoModFile, goModFilename, err) + } + + if err := config.InitMatchers(); err != nil { + return nil, err + } + + p := &Processor{ + Config: config, + Modfile: modFile, + } + + p.SetBlockedModules() + + return p, nil +} + +// ProcessFiles takes a string slice with file names (full paths) +// and lints them. +func (p *Processor) ProcessFiles(filenames []string) (issues []Issue) { + for _, filename := range filenames { + data, err := os.ReadFile(filepath.Clean(filename)) + if err != nil { + issues = append(issues, Issue{ + FileName: filename, + LineNumber: 0, + Reason: fmt.Sprintf("unable to read file, file cannot be linted (%s)", err.Error()), + }) + + continue + } + + issues = append(issues, p.process(filename, data)...) + } + + return issues +} + +// SetBlockedModules determines and sets which modules are blocked by reading +// the go.mod file of the current module. +// +// It works by iterating over the required modules specified in the require +// directive, checking if the module prefix or full name is in the allowed list. +// +// Rules are evaluated using a layered strategy for deterministic results: +// 1. Exact match — O(1) lookup; wins immediately. +// 2. Prefix match — longest matching prefix wins. +// 3. Regex match — evaluated in alphabetical key order; first match wins. +func (p *Processor) SetBlockedModules() { //nolint:gocognit // Ack this is a long func. + blockedModules := make(map[string][]string, len(p.Modfile.Require)) + currentModuleName := p.Modfile.Module.Mod.Path + requiredModules := p.Modfile.Require + + // Build tiered rule indices for blocked and allowed rules. + blockedIdx, blockedLookup := buildRuleIndex( + p.Config.Blocked, + func(r BlockedModule) string { return r.Module }, + func(r BlockedModule) MatchType { return r.MatchType }, + func(r BlockedModule) Matcher { return r.Matcher }, + ) + allowedIdx, allowedLookup := buildRuleIndex( + p.Config.Allowed, + func(r AllowedModule) string { return r.Module }, + func(r AllowedModule) MatchType { return r.MatchType }, + func(r AllowedModule) Matcher { return r.Matcher }, + ) + + for i := range requiredModules { + requiredModuleName := strings.TrimSpace(requiredModules[i].Mod.Path) + requiredModuleVersion := strings.TrimSpace(requiredModules[i].Mod.Version) + + var matchedBlockRule *BlockedModule + + // Check against blocked rules first (exact > longest prefix > first regex) + if key, ok := blockedIdx.bestMatch(requiredModuleName); ok { + rule := blockedLookup[key] // copy + matchedBlockRule = &rule + } + + if matchedBlockRule != nil && matchedBlockRule.IsCurrentModuleARecommendation(currentModuleName) { + // The current module is a recommended alternative for this blocked module, allowing it. + matchedBlockRule = nil + } + + if matchedBlockRule != nil { + isVersBlocked, err := matchedBlockRule.CheckVersion(requiredModuleVersion) + if err != nil { + // NOTE: Unreachable via real go.mod files; modfile.Parse rejects invalid versions + // earlier. Left untested by design as this branch cannot be triggered. + blockedModules[requiredModuleName] = append(blockedModules[requiredModuleName], + fmt.Sprintf("%s unable to parse version `%s`: %s", + blockReasonInBlockedList, requiredModuleVersion, err, + ), + ) + + continue + } + + if !isVersBlocked { + // Doesn't match the blocked version constraint, so we let it pass the block check + matchedBlockRule = nil + } + } + + // If it's blocked, record it and move to next + if matchedBlockRule != nil { + blockedModules[requiredModuleName] = append(blockedModules[requiredModuleName], + fmt.Sprintf("%s %s", blockReasonInBlockedList, + matchedBlockRule.BlockReason(requiredModuleVersion), + ), + ) + + continue + } + + // If no allowed list is specified, default mapping is to allow all + if len(p.Config.Allowed) == 0 { + continue + } + + isAllowed := false + + var matchedButWrongVersion *AllowedModule + + if key, ok := allowedIdx.bestMatch(requiredModuleName); ok { + rule := allowedLookup[key] // copy + + ok, err := rule.CheckVersion(requiredModuleVersion) + + switch { + case err != nil: + // NOTE: Unreachable via real go.mod files; modfile.Parse rejects invalid versions + // earlier. Left untested by design as this branch cannot be triggered. + blockedModules[requiredModuleName] = append(blockedModules[requiredModuleName], + fmt.Sprintf("import of package `%%s` is blocked because the module version `%s` could not be parsed: %s", + requiredModuleVersion, err, + ), + ) + + isAllowed = true // skip the generic "not allowed" message below + case ok: + isAllowed = true + default: + matchedButWrongVersion = &rule + } + } + + if !isAllowed { + blockedModules[requiredModuleName] = append(blockedModules[requiredModuleName], + fmt.Sprintf("import of package `%%s` is blocked because %s", matchedButWrongVersion.NotAllowedReason(requiredModuleVersion))) + } + } + + // Blocks local 'replace' directives to prevent committing dev overrides. + // Legitimate sibling modules in multi-module repos (sharing the same + // module name) are exempt. + if p.Config.LocalReplaceDirectives { + for _, r := range p.Modfile.Replace { + if isBlockedLocalReplace(r) { + blockedModules[r.Old.Path] = append(blockedModules[r.Old.Path], + blockReasonHasLocalReplaceDirective, + ) + } + } + } + + p.blockedModulesFromModFile = blockedModules +} + +// buildRuleIndex constructs a ruleIndex and a key→rule lookup from any slice of rules. +// The three accessor functions extract the module name, match type, and compiled matcher +// from each rule, keeping this function independent of the concrete rule type. +func buildRuleIndex[R any]( + rules []R, + moduleFn func(R) string, + matchTypeFn func(R) MatchType, + matcherFn func(R) Matcher, +) (*ruleIndex, map[string]R) { + keys := make([]string, 0, len(rules)) + matchTypes := make(map[string]MatchType, len(rules)) + matchers := make(map[string]Matcher, len(rules)) + lookup := make(map[string]R, len(rules)) + + for _, r := range rules { + mod := moduleFn(r) + keys = append(keys, mod) + matchTypes[mod] = matchTypeFn(r) + matchers[mod] = matcherFn(r) + lookup[mod] = r + } + + return newRuleIndex(keys, matchTypes, matchers), lookup +} + +// process file imports and add lint error if blocked package is imported. +func (p *Processor) process(filename string, data []byte) (issues []Issue) { + fileSet := token.NewFileSet() + + file, err := parser.ParseFile(fileSet, filename, data, parser.ParseComments) + if err != nil { + issues = append(issues, Issue{ + FileName: filename, + LineNumber: 0, + Reason: fmt.Sprintf("invalid syntax, file cannot be linted (%s)", err.Error()), + }) + + return + } + + imports := file.Imports + for n := range imports { + importedPkg := strings.TrimSpace(strings.Trim(imports[n].Path.Value, "\"")) + + blockReasons := p.isBlockedPackageFromModFile(importedPkg) + if blockReasons == nil { + continue + } + + for _, blockReason := range blockReasons { + issues = append(issues, p.addError(fileSet, imports[n].Pos(), blockReason)) + } + } + + return issues +} + +// addError adds an error for the file and line number for the current token.Pos +// with the given reason. +func (p *Processor) addError(fileset *token.FileSet, pos token.Pos, reason string) Issue { + position := fileset.Position(pos) + + return Issue{ + FileName: position.Filename, + LineNumber: position.Line, + Position: position, + Reason: reason, + } +} + +// isBlockedPackageFromModFile returns the block reason if the package is blocked. +func (p *Processor) isBlockedPackageFromModFile(packageName string) []string { + for blockedModuleName, blockReasons := range p.blockedModulesFromModFile { + if isPackageInModule(packageName, blockedModuleName) { + formattedReasons := make([]string, 0, len(blockReasons)) + + for _, blockReason := range blockReasons { + formattedReasons = append(formattedReasons, fmt.Sprintf(blockReason, packageName)) + } + + return formattedReasons + } + } + + return nil +} + +// loadGoModFile loads the contents of the go.mod file in the current working directory. +// It first checks the "GOMOD" environment variable to determine the path of the go.mod file. +// If the environment variable is not set or the file does not exist, it falls back to reading the go.mod file in the current directory. +// If the "GOMOD" environment variable is set to "/dev/null", it returns an error indicating that the current working directory must have a go.mod file. +// The function returns the contents of the go.mod file as a byte slice and any error encountered during the process. +func loadGoModFile() ([]byte, error) { + cmd := exec.Command("go", "env", "-json") //nolint:noctx // Ack at some point might use os/exec.CommandContext. + stdout, _ := cmd.StdoutPipe() + _ = cmd.Start() + + if stdout == nil { + return os.ReadFile(filepath.Clean(goModFilename)) + } + + buf := new(bytes.Buffer) + _, _ = buf.ReadFrom(stdout) + + goEnv := make(map[string]string) + + err := json.Unmarshal(buf.Bytes(), &goEnv) + if err != nil { + return os.ReadFile(goModFilename) + } + + if _, ok := goEnv["GOMOD"]; !ok { + return os.ReadFile(goModFilename) + } + + if _, err = os.Stat(goEnv["GOMOD"]); os.IsNotExist(err) { + return os.ReadFile(goModFilename) + } + + if goEnv["GOMOD"] == "/dev/null" || goEnv["GOMOD"] == "NUL" { + return nil, errors.New("current working directory must have a go.mod file") + } + + return os.ReadFile(goEnv["GOMOD"]) +} + +// isBlockedLocalReplace returns true if the replace directive points to a local +// filesystem path that is not a legitimate sibling module. +func isBlockedLocalReplace(r *modfile.Replace) bool { + if r.New.Path == "" || r.New.Version != "" { + return false + } + + replacePath := r.New.Path + if !filepath.IsAbs(replacePath) { + wd, err := os.Getwd() + if err != nil { + wd = "." + } + + replacePath = filepath.Join(wd, replacePath) + } + + return !isModuleAtPath(replacePath, r.Old.Path) +} + +// isModuleAtPath returns true if the directory at path contains a go.mod file +// that declares moduleName as its module, indicating a legitimate sibling module +// in a multi-module repository rather than a local development override. +func isModuleAtPath(path, moduleName string) bool { + data, err := os.ReadFile(filepath.Clean(filepath.Join(path, goModFilename))) + if err != nil { + return false + } + + mf, err := modfile.Parse(goModFilename, data, nil) + if err != nil { + return false + } + + return mf.Module.Mod.Path == moduleName +} + +// isPackageInModule determines if a package is a part of the specified Go module. +func isPackageInModule(pkg, mod string) bool { + // Split pkg and mod paths into parts + pkgPart := strings.Split(pkg, "/") + modPart := strings.Split(mod, "/") + + pkgPartMatches := 0 + + // Count number of times pkg path matches the mod path + for i, m := range modPart { + if len(pkgPart) > i && pkgPart[i] == m { + pkgPartMatches++ + } + } + + // If pkgPartMatches are not the same length as modPart + // than the package is not in this module + if pkgPartMatches != len(modPart) { + return false + } + + if len(pkgPart) > len(modPart) { + // If pkgPart path starts with a major version + // than the package is not in this module as + // major versions are completely different modules + if startsWithVersion.MatchString(pkgPart[len(modPart)]) { + return false + } + } + + return true +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/.golangci.yml b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.golangci.yml index 6534d531..94a3eca3 100644 --- a/vendor/github.com/santhosh-tekuri/jsonschema/v6/.golangci.yml +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.golangci.yml @@ -1,4 +1,3 @@ -version: "2" linters: enable: - nakedret diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/.swp b/vendor/github.com/santhosh-tekuri/jsonschema/v6/.swp deleted file mode 100644 index e5119003..00000000 Binary files a/vendor/github.com/santhosh-tekuri/jsonschema/v6/.swp and /dev/null differ diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/format.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/format.go index b78b22e2..0d7f5903 100644 --- a/vendor/github.com/santhosh-tekuri/jsonschema/v6/format.go +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/format.go @@ -318,7 +318,7 @@ func validateEmail(v any) error { return LocalizableError("local part more than 64 characters long") } - if len(local) > 1 && strings.HasPrefix(local, `"`) && strings.HasPrefix(local, `"`) { + if len(local) > 1 && strings.HasPrefix(local, `"`) && strings.HasSuffix(local, `"`) { // quoted local := local[1 : len(local)-1] if strings.IndexByte(local, '\\') != -1 || strings.IndexByte(local, '"') != -1 { diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/go.work.sum b/vendor/github.com/santhosh-tekuri/jsonschema/v6/go.work.sum index 2b5b811d..c268feb5 100644 --- a/vendor/github.com/santhosh-tekuri/jsonschema/v6/go.work.sum +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/go.work.sum @@ -1,4 +1,4 @@ -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/kind/kind.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/kind/kind.go index a37fb0b9..62efa755 100644 --- a/vendor/github.com/santhosh-tekuri/jsonschema/v6/kind/kind.go +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/kind/kind.go @@ -511,7 +511,7 @@ func (*ContentMediaType) KeywordPath() []string { } func (k *ContentMediaType) LocalizedString(p *message.Printer) string { - return p.Sprintf("value if not of mediatype %s: %v", quote(k.Want), k.Err) + return p.Sprintf("value is not of mediatype %s: %v", quote(k.Want), k.Err) } // -- diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/objcompiler.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/objcompiler.go index f1494b13..d47efa89 100644 --- a/vendor/github.com/santhosh-tekuri/jsonschema/v6/objcompiler.go +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/objcompiler.go @@ -355,9 +355,13 @@ func (c *objCompiler) enqueueRef(pname string) (*Schema, error) { if ref == nil { return nil, nil } + return c.enqueueRefVal(*ref) +} + +func (c *objCompiler) enqueueRefVal(ref string) (*Schema, error) { baseURL := c.res.id // baseURL := c.r.baseURL(c.up.ptr) - uf, err := baseURL.join(*ref) + uf, err := baseURL.join(ref) if err != nil { return nil, err } diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/schema.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/schema.go index b4c1f37a..ab2474fa 100644 --- a/vendor/github.com/santhosh-tekuri/jsonschema/v6/schema.go +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/schema.go @@ -1,8 +1,25 @@ +/* +Package jsonschema provides json-schema compilation and validation. + +The schema is compiled against the version specified in "$schema" property. +If "$schema" property is missing, it uses latest draft which currently implemented +by this library. + +You can force to use specific draft, when "$schema" is missing, as follows: + + compiler := jsonschema.NewCompiler() + compiler.DefaultDraft(jsonschema.Draft4) + +This package supports loading json-schema from filePath and fileURL. + +see examples for usage. +*/ package jsonschema import ( "encoding/json" "fmt" + "math" "math/big" ) @@ -110,12 +127,22 @@ const ( ) func typeOf(v any) jsonType { - switch v.(type) { + switch v := v.(type) { case nil: return nullType case bool: return booleanType - case json.Number, float32, float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + case float64: + if math.IsNaN(v) || math.IsInf(v, 0) { + return invalidType + } + return numberType + case float32: + if math.IsNaN(float64(v)) || math.IsInf(float64(v), 0) { + return invalidType + } + return numberType + case json.Number, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: return numberType case string: return stringType diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/util.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/util.go index c6f8e775..8cd32aae 100644 --- a/vendor/github.com/santhosh-tekuri/jsonschema/v6/util.go +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/util.go @@ -320,6 +320,9 @@ func equals(v1, v2 any) (bool, ErrorKind) { v2, ok := v2.(string) return ok && v1 == v2, nil case json.Number, float32, float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + if typeOf(v2) != numberType { + return false, nil + } num1, ok1 := new(big.Rat).SetString(fmt.Sprint(v1)) num2, ok2 := new(big.Rat).SetString(fmt.Sprint(v2)) return ok1 && ok2 && num1.Cmp(num2) == 0, nil diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/validator.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/validator.go index e2ace37a..53a4c730 100644 --- a/vendor/github.com/santhosh-tekuri/jsonschema/v6/validator.go +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/validator.go @@ -293,6 +293,7 @@ func (vd *validator) objValidate(obj map[string]any) { } if err := sch.validate(pname, vd.regexpEngine, meta, resources, vd.assertVocabs, vd.vocabularies); err != nil { verr := err.(*ValidationError) + verr.InstanceLocation = vd.vloc verr.SchemaURL = s.PropertyNames.Location verr.ErrorKind = &kind.PropertyNames{Property: pname} vd.addErr(verr) @@ -375,7 +376,7 @@ func (vd *validator) arrValidate(arr []any) { } case *Schema: for i, item := range arr[evaluated:] { - vd.addErr(vd.validateVal(additional, item, strconv.Itoa(i))) + vd.addErr(vd.validateVal(additional, item, strconv.Itoa(evaluated+i))) } } } @@ -390,7 +391,7 @@ func (vd *validator) arrValidate(arr []any) { // items2020 -- if s.Items2020 != nil { for i, item := range arr[evaluated:] { - vd.addErr(vd.validateVal(s.Items2020, item, strconv.Itoa(i))) + vd.addErr(vd.validateVal(s.Items2020, item, strconv.Itoa(evaluated+i))) } } } @@ -503,6 +504,7 @@ func (vd *validator) strValidate(str string) { } if err = sch.validate(*deserialized, vd.regexpEngine, meta, resources, vd.assertVocabs, vd.vocabularies); err != nil { verr := err.(*ValidationError) + verr.InstanceLocation = vd.vloc verr.SchemaURL = s.Location verr.ErrorKind = &kind.ContentSchema{} vd.addErr(verr) diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/v6/vocab.go b/vendor/github.com/santhosh-tekuri/jsonschema/v6/vocab.go index c81cb700..eaed8c95 100644 --- a/vendor/github.com/santhosh-tekuri/jsonschema/v6/vocab.go +++ b/vendor/github.com/santhosh-tekuri/jsonschema/v6/vocab.go @@ -14,6 +14,10 @@ func (ctx *CompilerContext) Enqueue(schPath []string) *Schema { return ctx.c.enqueuePtr(ptr) } +func (ctx *CompilerContext) EnqueueRef(ref string) (*Schema, error) { + return ctx.c.enqueueRefVal(ref) +} + // Vocabulary defines a set of keywords, their syntax and // their semantics. type Vocabulary struct { diff --git a/vendor/github.com/securego/gosec/v2/.env.example b/vendor/github.com/securego/gosec/v2/.env.example new file mode 100644 index 00000000..0a7ec71e --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/.env.example @@ -0,0 +1,3 @@ +GOSEC_AI_API_KEY=your_ai_api_key +GOSEC_AI_PROVIDER=atlas +GOSEC_AI_BASE_URL=https://api.atlascloud.ai/v1 diff --git a/vendor/github.com/securego/gosec/v2/.gitignore b/vendor/github.com/securego/gosec/v2/.gitignore index d9f5c1fc..eac8f99d 100644 --- a/vendor/github.com/securego/gosec/v2/.gitignore +++ b/vendor/github.com/securego/gosec/v2/.gitignore @@ -33,10 +33,12 @@ coverage.out *.prof .DS_Store +.env.local +.env.*.local .vscode .idea # SBOMs generated during CI /bom.json -1 \ No newline at end of file +1 diff --git a/vendor/github.com/securego/gosec/v2/CLAUDE.md b/vendor/github.com/securego/gosec/v2/CLAUDE.md new file mode 100644 index 00000000..f3e0b72d --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/CLAUDE.md @@ -0,0 +1,51 @@ +# gosec - Go Security Checker + +gosec is a Go static analysis tool that inspects Go source code for security vulnerabilities by scanning the Go AST and SSA form. + +## Build & Test + +```bash +# Build +go build ./cmd/gosec/ + +# Run all tests +go test ./... + +# Run a specific test +go test -run TestName ./path/to/package/ + +# Lint +golangci-lint run + +# Run gosec against a sample file +go run ./cmd/gosec/ ./path/to/sample.go +``` + +## Code Style + +- Idiomatic Go; follow existing patterns in the codebase. +- Prefer SSA-based analyzers over AST-based rules when feasible. +- Optimize for performance — avoid unnecessary repeated AST or SSA traversals. + +## Project Structure + +- `rules/` — AST-based rule implementations +- `analyzers/` — SSA-based analyzer implementations +- `cmd/gosec/` — CLI entry point +- `testutils/` — sample files used in tests (positive and negative cases) +- `issue/` — issue and CWE type definitions +- `report/` — output formatters + +## Adding Rules + +- Select an appropriate CWE aligned with current repository mappings. +- Integrate the rule in all required registration points. +- Add sample files in `testutils/` with at least 2 positive and 2 negative cases. +- Update rule documentation in `README.md` in the same style as other rules. + +## Custom Commands + +- `/create-gosec-rule` — Design and implement a new gosec rule from an issue description +- `/fix-gosec-bug` — Investigate and fix a bug from a GitHub issue URL +- `/update-go-versions` — Bump supported Go versions across the repo +- `/update-action-version` — Update the gosec GHCR image version in action.yml diff --git a/vendor/github.com/securego/gosec/v2/DEVELOPMENT.md b/vendor/github.com/securego/gosec/v2/DEVELOPMENT.md index f32526d5..5a8a0e3c 100644 --- a/vendor/github.com/securego/gosec/v2/DEVELOPMENT.md +++ b/vendor/github.com/securego/gosec/v2/DEVELOPMENT.md @@ -97,7 +97,7 @@ func runMyAnalyzer(pass *analysis.Pass) (interface{}, error) { ### Creating taint analysis rules gosec taint analyzers track data flow from untrusted sources to dangerous sinks. -Current taint rules include SQL injection, command injection, path traversal, SSRF, XSS, log injection, and SMTP injection. +Current taint rules include SQL injection, command injection, path traversal, SSRF, XSS, log injection, SMTP injection, server-side template injection, unsafe deserialization, and open redirect. #### Steps diff --git a/vendor/github.com/securego/gosec/v2/Makefile b/vendor/github.com/securego/gosec/v2/Makefile index 6dd8c4c3..f91529ca 100644 --- a/vendor/github.com/securego/gosec/v2/Makefile +++ b/vendor/github.com/securego/gosec/v2/Makefile @@ -33,6 +33,9 @@ install-govulncheck: test: build-race fmt vet sec govulncheck go run github.com/onsi/ginkgo/v2/ginkgo -- --ginkgo.v --ginkgo.fail-fast +test-nocache: build-race fmt vet sec govulncheck + go test -count=1 -v ./... + fmt: @echo "FORMATTING" @FORMATTED=`$(GO) fmt ./...` @@ -99,4 +102,4 @@ tlsconfig: perf-diff: ./perf-diff.sh -.PHONY: test build clean release image image-push tlsconfig perf-diff +.PHONY: test test-nocache build clean release image image-push tlsconfig perf-diff diff --git a/vendor/github.com/securego/gosec/v2/README.md b/vendor/github.com/securego/gosec/v2/README.md index 6f6d27d1..c3362fa9 100644 --- a/vendor/github.com/securego/gosec/v2/README.md +++ b/vendor/github.com/securego/gosec/v2/README.md @@ -1,7 +1,8 @@ # gosec - Go Security Checker -Inspects source code for security problems by scanning the Go AST and SSA code representation. +Inspects source code for security problems by scanning the Go AST +and SSA code representation. @@ -14,20 +15,23 @@ Inspects source code for security problems by scanning the Go AST and SSA code r - [Selecting rules](#selecting-rules) - [Output formats](#output-formats) -> ⚠️ Container image migration notice: `gosec` images was migrated from Docker Hub to `ghcr.io/securego/gosec`. -> Starting with release `v2.24.7` the image is no longer published in Docker Hub. - ## Features -- **Pattern-based rules** for detecting common security issues in Go code -- **SSA-based analyzers** for type conversions, slice bounds, and crypto issues -- **Taint analysis** for tracking data flow from user input to dangerous functions (SQL injection, command injection, path traversal, SSRF, XSS, log injection) +- **Pattern-based rules** for detecting common security issues + in Go code +- **SSA-based analyzers** for type conversions, slice bounds, + and crypto issues +- **Taint analysis** for tracking data flow from user input to + dangerous functions (SQL injection, command injection, path + traversal, SSRF, XSS, log injection, SMTP injection, SSTI, + unsafe deserialization, open redirect) ## License 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 [here](http://www.apache.org/licenses/LICENSE-2.0). +You may obtain a copy of the License +[here](http://www.apache.org/licenses/LICENSE-2.0). ## Project status @@ -48,7 +52,8 @@ You may obtain a copy of the License [here](http://www.apache.org/licenses/LICEN You can run `gosec` as a GitHub action as follows: -Use the versioned tag with `@master` which is pinned to the latest stable release. This will provide a stable behavior. +Use the versioned tag with `@master` which is pinned to the +latest stable release. This will provide a stable behavior. ```yaml name: Run Gosec @@ -75,10 +80,16 @@ jobs: #### Scanning Projects with Private Modules -If your project imports private Go modules, you need to configure authentication so that `gosec` can fetch the dependencies. Set the following environment variables in your workflow: +If your project imports private Go modules, you need to +configure authentication so that `gosec` can fetch the +dependencies. Set the following environment variables in +your workflow: -- `GOPRIVATE`: A comma-separated list of module path prefixes that should be considered private (e.g., `github.com/your-org/*`). -- `GITHUB_AUTHENTICATION_TOKEN`: A GitHub token with read access to your private repositories. +- `GOPRIVATE`: A comma-separated list of module path prefixes + that should be considered private + (e.g., `github.com/your-org/*`). +- `GITHUB_AUTHENTICATION_TOKEN`: A GitHub token with read + access to your private repositories. ```yaml name: Run Gosec @@ -107,9 +118,13 @@ jobs: ### Integrating with code scanning -You can [integrate third-party code analysis tools](https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/integrating-with-code-scanning) with GitHub code scanning by uploading data as SARIF files. +You can [integrate third-party code analysis tools](https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/integrating-with-code-scanning) +with GitHub code scanning by uploading data as SARIF files. -The workflow shows an example of running the `gosec` as a step in a GitHub action workflow which outputs the `results.sarif` file. The workflow then uploads the `results.sarif` file to GitHub using the `upload-sarif` action. +The workflow shows an example of running the `gosec` as a step +in a GitHub action workflow which outputs the `results.sarif` +file. The workflow then uploads the `results.sarif` file to +GitHub using the `upload-sarif` action. ```yaml name: "Security Scan" @@ -143,7 +158,12 @@ jobs: ### Go Analysis -The `goanalysis` package provides a [`golang.org/x/tools/go/analysis.Analyzer`](https://pkg.go.dev/golang.org/x/tools/go/analysis) for integration with tools that support the standard Go analysis interface, such as Bazel's [nogo](https://github.com/bazelbuild/rules_go/blob/master/go/nogo.rst) framework: +The `goanalysis` package provides a +[`golang.org/x/tools/go/analysis.Analyzer`](https://pkg.go.dev/golang.org/x/tools/go/analysis) +for integration with tools that support the standard Go +analysis interface, such as Bazel's +[nogo](https://github.com/bazelbuild/rules_go/blob/master/go/nogo.rst) +framework: ```starlark nogo( @@ -185,34 +205,48 @@ gosec -fmt sarif -out results.sarif ./... ## Usage -Gosec can be configured to only run a subset of rules, to exclude certain file -paths, and produce reports in different formats. By default all rules will be -run against the supplied input files. To recursively scan from the current -directory you can supply `./...` as the input argument. +Gosec can be configured to only run a subset of rules, to +exclude certain file paths, and produce reports in different +formats. By default all rules will be run against the supplied +input files. To recursively scan from the current directory you +can supply `./...` as the input argument. ### Available rules gosec includes rules across these categories: -- `G1xx`: general secure coding issues (for example hardcoded credentials, unsafe usage, HTTP hardening) -- `G2xx`: injection risks in query/template/command construction -- `G3xx`: file and path handling risks (permissions, traversal, temp files, archive extraction) +- `G1xx`: general secure coding issues (for example hardcoded + credentials, unsafe usage, HTTP hardening, cookie security) +- `G2xx`: injection risks in query/template/command + construction +- `G3xx`: file and path handling risks (permissions, traversal, + temp files, archive extraction) - `G4xx`: crypto and TLS weaknesses - `G5xx`: blocklisted imports -- `G6xx`: Go-specific correctness/security checks (for example range aliasing and slice bounds) -- `G7xx`: taint analysis rules (SQL injection, command injection, path traversal, SSRF, XSS, log and SMTP injection) +- `G6xx`: Go-specific correctness/security checks (for example + range aliasing and slice bounds) +- `G7xx`: taint analysis rules (SQL injection, command + injection, path traversal, SSRF, XSS, log, SMTP injection, + SSTI, unsafe deserialization, and open redirect) -For the full list, rule descriptions, and per-rule configuration, see [RULES.md](RULES.md). +For the full list, rule descriptions, and per-rule +configuration, see [RULES.md](RULES.md). ### 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/) +- 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 -By default, gosec will run all rules against the supplied file paths. It is however possible to select a subset of rules to run via the `-include=` flag, -or to specify a set of rules to explicitly exclude using the `-exclude=` flag. +By default, gosec will run all rules against the supplied file +paths. It is however possible to select a subset of rules to +run via the `-include=` flag, or to specify a set of rules to +explicitly exclude using the `-exclude=` flag. ```bash # Run a specific set of rules @@ -224,11 +258,16 @@ $ 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/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 -A number of global settings can be provided in a configuration file as follows: +A number of global settings can be provided in a configuration +file as follows: ```JSON { @@ -239,8 +278,10 @@ A number of global settings can be provided in a configuration file as follows: } ``` -- `nosec`: this setting will overwrite all `#nosec` directives defined throughout the code base -- `audit`: runs in audit mode which enables addition checks that for normal code analysis might be too nosy +- `nosec`: this setting will overwrite all `#nosec` directives + defined throughout the code base +- `audit`: runs in audit mode which enables addition checks + that for normal code analysis might be too nosy ```bash # Run with a global configuration file @@ -249,9 +290,9 @@ $ gosec -conf config.json . ### Path-Based Rule Exclusions -Large repositories with multiple components may need different security rules -for different paths. Use `exclude-rules` to suppress specific rules for specific -paths. +Large repositories with multiple components may need different +security rules for different paths. Use `exclude-rules` to +suppress specific rules for specific paths. **Configuration File:** ```json @@ -283,22 +324,31 @@ gosec --exclude-rules="cmd/.*:G204,G304;test/.*:G101" ./... | Field | Type | Description | |-------|------|-------------| -| `path` | string (regex) | Regular expression matched against file paths | -| `rules` | []string | Rule IDs to exclude. Use `*` to exclude all rules | +| `path` | string (regex) | Regex matched against file paths | +| `rules` | []string | Rule IDs to exclude. `*` for all | #### Rule Configuration -Some rules accept configuration flags as well; these flags are documented in [RULES.md](https://github.com/securego/gosec/blob/master/RULES.md). +Some rules accept configuration flags as well; these flags are +documented in +[RULES.md](https://github.com/securego/gosec/blob/master/RULES.md). #### Go version -Some rules require a specific Go version which is retrieved from the Go module file present in the project. If this version cannot be found, it will fallback to Go runtime version. +Some rules require a specific Go version which is retrieved +from the Go module file present in the project. If this version +cannot be found, it will fallback to Go runtime version. -The Go module version is parsed using the `go list` command which in some cases might lead to performance degradation. In this situation, the go module version can be easily provided by setting the environment variable `GOSECGOVERSION=go1.21.1`. +The Go module version is parsed using the `go list` command +which in some cases might lead to performance degradation. In +this situation, the go module version can be easily provided by +setting the environment variable +`GOSECGOVERSION=go1.21.1`. ### Dependencies -gosec loads packages using Go modules. In most projects, dependencies are resolved automatically during scanning. +gosec loads packages using Go modules. In most projects, +dependencies are resolved automatically during scanning. If dependencies are missing, run: @@ -309,9 +359,11 @@ go mod download ### Excluding test files and folders -gosec will ignore test files across all packages and any dependencies in your vendor directory. +gosec will ignore test files across all packages and any +dependencies in your vendor directory. -The scanning of test files can be enabled with the following flag: +The scanning of test files can be enabled with the following +flag: ```bash gosec -tests ./... @@ -325,7 +377,8 @@ Also additional folders can be excluded as follows: ### Excluding generated files -gosec can ignore generated go files with default generated code comment. +gosec can ignore generated go files with default generated +code comment. ``` // Code generated by some generator DO NOT EDIT. @@ -337,36 +390,83 @@ gosec -exclude-generated ./... ### Auto fixing vulnerabilities -gosec can suggest fixes based on AI recommendation. It will call an AI API to receive a suggestion for a security finding. - -You can enable this feature by providing the following command line arguments: - -- `ai-api-provider`: the name of the AI API provider. Supported providers: - - **Gemini**: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-2.5-flash-lite`, `gemini-2.0-flash`, `gemini-2.0-flash-lite` (default) - - **Claude**: `claude-sonnet-4-0` (default), `claude-opus-4-0`, `claude-opus-4-1`, `claude-sonnet-3-7` - - **OpenAI**: `gpt-4o` (default), `gpt-4o-mini` - - **Custom OpenAI-compatible**: Any custom model name (requires `ai-base-url`) -- `ai-api-key` or set the environment variable `GOSEC_AI_API_KEY`: the key to access the AI API - - For Gemini, you can create an API key following [these instructions](https://ai.google.dev/gemini-api/docs/api-key) - - For Claude, get your API key from [Anthropic Console](https://console.anthropic.com/) - - For OpenAI, get your API key from [OpenAI Platform](https://platform.openai.com/api-keys) -- `ai-base-url`: (optional) custom base URL for OpenAI-compatible APIs (e.g., Azure OpenAI, LocalAI, Ollama) -- `ai-skip-ssl`: (optional) skip SSL certificate verification for AI API (useful for self-signed certificates) +gosec can suggest fixes based on AI recommendation. It will +call an AI API to receive a suggestion for a security finding. + +You can enable this feature by providing the following command +line arguments: + +- `ai-api-provider`: the name of the AI API provider. + Supported providers: + - **Atlas Cloud**: `atlas` (default model + `deepseek-ai/deepseek-v4-flash`), + `atlas-deepseek-v4-flash`, + `atlas-qwen3-coder-next`, `atlas-kimi-k2.6`, or + `atlas:` for any Atlas Cloud hosted chat model. + Atlas Cloud is an OpenAI-compatible provider available at + [atlascloud.ai](https://www.atlascloud.ai/?utm_source=github&utm_medium=link&utm_campaign=gosec) + - **Gemini**: `gemini-3-pro-preview` (default), + `gemini-2.5-pro`, `gemini-2.5-flash`, + `gemini-2.5-flash-lite` + - **Claude**: `claude-sonnet-4-6` (default), + `claude-opus-4-7`, `claude-opus-4-6`, + `claude-sonnet-4-5`, `claude-opus-4-5`, + `claude-haiku-4-5` + - **OpenAI**: `gpt-5.4` (default), `gpt-5.4-mini`, + `gpt-5.4-nano` + - **Custom OpenAI-compatible**: Any custom model name + (requires `ai-base-url`) +- `ai-api-key` or set the environment variable + `GOSEC_AI_API_KEY`: the key to access the AI API + - For Gemini, you can create an API key following + [these instructions](https://ai.google.dev/gemini-api/docs/api-key) + - For Claude, get your API key from + [Anthropic Console](https://console.anthropic.com/) + - For OpenAI, get your API key from + [OpenAI Platform](https://platform.openai.com/api-keys) +- `ai-base-url`: (optional) custom base URL for + OpenAI-compatible APIs (e.g., Azure OpenAI, LocalAI, + Ollama) + - Atlas Cloud uses `https://api.atlascloud.ai/v1` by default, + so `ai-base-url` is optional for the built-in `atlas` + provider +- `GOSEC_AI_PROVIDER`: (optional) environment variable + alternative to `ai-api-provider` +- `GOSEC_AI_BASE_URL`: (optional) environment variable + alternative to `ai-base-url` +- `ai-skip-ssl`: (optional) skip SSL certificate verification + for AI API (useful for self-signed certificates) + +> 🎁 **[Atlas Cloud](https://www.atlascloud.ai/?utm_source=github&utm_medium=link&utm_campaign=gosec)** is a full-modal AI inference platform that gives developers a single AI API to access video generation, image generation, and LLM APIs. Instead of managing multiple vendor integrations, you connect once and get unified access to 300+ curated models across all modalities. +> +> Check out Atlas Cloud's new coding plan promotion for more budget-friendly API access: [https://www.atlascloud.ai/console/coding-plan](https://www.atlascloud.ai/console/coding-plan) **Examples:** ```bash +# Using Atlas Cloud with the default DeepSeek V4 Flash model +export GOSEC_AI_API_KEY="your_key" +export GOSEC_AI_PROVIDER="atlas" +gosec ./... + +# Using Atlas Cloud with an explicit hosted model +GOSEC_AI_API_KEY="your_key" \ + gosec -ai-api-provider="atlas:qwen/qwen3-coder-next" ./... + # Using Gemini -gosec -ai-api-provider="gemini-2.0-flash" -ai-api-key="your_key" ./... +gosec -ai-api-provider="gemini-3-pro-preview" \ + -ai-api-key="your_key" ./... # Using Claude -gosec -ai-api-provider="claude-sonnet-4-0" -ai-api-key="your_key" ./... +gosec -ai-api-provider="claude-sonnet-4-6" \ + -ai-api-key="your_key" ./... # Using OpenAI -gosec -ai-api-provider="gpt-4o" -ai-api-key="your_key" ./... +gosec -ai-api-provider="gpt-5.4" \ + -ai-api-key="your_key" ./... # Using Azure OpenAI -gosec -ai-api-provider="gpt-4o" \ +gosec -ai-api-provider="gpt-5.4" \ -ai-api-key="your_azure_key" \ -ai-base-url="https://your-resource.openai.azure.com/openai/deployments/your-deployment" \ ./... @@ -386,13 +486,16 @@ gosec -ai-api-provider="custom-model" \ ### Annotating code -As with all automated detection tools, there will be cases of false positives. -In cases where gosec reports a failure that has been manually verified as being safe, -it is possible to annotate the code with a comment that starts with `#nosec`. +As with all automated detection tools, there will be cases of +false positives. In cases where gosec reports a failure that +has been manually verified as being safe, it is possible to +annotate the code with a comment that starts with `#nosec`. -The `#nosec` comment should have the format `#nosec [RuleList] [- Justification]`. +The `#nosec` comment should have the format +`#nosec [RuleList] [-- Justification]`. -The `#nosec` comment needs to be placed on the line where the warning is reported. +The `#nosec` comment needs to be placed on the line where the +warning is reported. ```go func main() { @@ -410,51 +513,92 @@ func main() { } ``` -When a specific false positive has been identified and verified as safe, you may -wish to suppress only that single rule (or a specific set of rules) within a section of code, -while continuing to scan for other problems. To do this, you can list the rule(s) to be suppressed within -the `#nosec` annotation, e.g: `/* #nosec G401 */` or `//#nosec G201 G202 G203` +When a specific false positive has been identified and verified +as safe, you may wish to suppress only that single rule (or a +specific set of rules) within a section of code, while +continuing to scan for other problems. To do this, you can list +the rule(s) to be suppressed within the `#nosec` annotation, +e.g: `/* #nosec G401 */` or `//#nosec G201 G202 G203` -You could put the description or justification text for the annotation. The -justification should be after the rule(s) to suppress and start with two or -more dashes, e.g: `//#nosec G101 G102 -- This is a false positive` +You could put the description or justification text for the +annotation. The justification should be after the rule(s) to +suppress and start with two or more dashes, +e.g: `//#nosec G101 G102 -- This is a false positive` -Alternatively, gosec also supports the `//gosec:disable` directive, which functions similar to `#nosec`: +Alternatively, gosec also supports the `//gosec:disable` +directive, which functions similar to `#nosec`: ```go //gosec:disable G101 -- This is a false positive ``` -In some cases you may also want to revisit places where `#nosec` or `//gosec:disable` annotations -have been used. To run the scanner and ignore any `#nosec` annotations you -can do the following: +In some cases you may also want to revisit places where +`#nosec` or `//gosec:disable` annotations have been used. To +run the scanner and ignore any `#nosec` annotations you can do +the following: ```bash gosec -nosec=true ./... ``` +#### Requiring rule IDs and justifications + +To prevent annotations from inadvertently suppressing unrelated +rules, or from being added without explanation, gosec accepts +two opt-in flags. Both default to `false`, so existing +codebases keep working unchanged. + +- `-nosec-require-rules` rejects naked `#nosec` / + `//gosec:disable` directives that do not list any rule ID. +- `-nosec-require-justification` rejects directives that do + not carry a `-- justification` after the rule list. + +When enabled, a directive that fails the check no longer +suppresses any finding and is reported as an error in the +output, alongside any underlying issue on the line. + +```bash +gosec -nosec-require-rules -nosec-require-justification ./... +``` + +The same options can be set via the global config block: + +```json +{ + "global": { + "nosec-require-rules": "enabled", + "nosec-require-justification": "enabled" + } +} +``` + ### Tracking suppressions -As described above, we could suppress violations externally (using `-include`/ -`-exclude`) or inline (using `#nosec` annotations). Suppression metadata can be emitted for auditing. +As described above, we could suppress violations externally +(using `-include`/`-exclude`) or inline (using `#nosec` +annotations). Suppression metadata can be emitted for auditing. Enable suppression tracking with `-track-suppressions`: ```bash -gosec -track-suppressions -exclude=G101 -fmt=sarif -out=results.sarif ./... +gosec -track-suppressions -exclude=G101 \ + -fmt=sarif -out=results.sarif ./... ``` -- For external suppressions, gosec records suppression info where `kind` is -`external` and `justification` is `Globally suppressed.`. -- For inline suppressions, gosec records suppression info where `kind` is -`inSource` and `justification` is the text after two or more dashes in the -comment. +- For external suppressions, gosec records suppression info + where `kind` is `external` and `justification` is + `Globally suppressed.`. +- For inline suppressions, gosec records suppression info + where `kind` is `inSource` and `justification` is the text + after two or more dashes in the comment. -**Note:** Only SARIF and JSON formats support tracking suppressions. +**Note:** Only SARIF and JSON formats support tracking +suppressions. ### Build tags -gosec is able to pass your [Go build tags](https://pkg.go.dev/go/build/) to the analyzer. +gosec is able to pass your +[Go build tags](https://pkg.go.dev/go/build/) to the analyzer. They can be provided as a comma separated list as follows: ```bash @@ -463,9 +607,12 @@ gosec -tags debug,ignore ./... ### Output formats -gosec supports `text`, `json`, `yaml`, `csv`, `junit-xml`, `html`, `sonarqube`, `golint`, and `sarif`. By default, -results will be reported to stdout, but can also be written to an output -file. The output format is controlled by the `-fmt` flag, and the output file is controlled by the `-out` flag as follows: +gosec supports `text`, `json`, `yaml`, `csv`, `junit-xml`, +`html`, `sonarqube`, `golint`, and `sarif`. By default, +results will be reported to stdout, but can also be written to +an output file. The output format is controlled by the `-fmt` +flag, and the output file is controlled by the `-out` flag as +follows: ```bash # Write output in json format to results.json @@ -473,16 +620,22 @@ $ gosec -fmt=json -out=results.json *.go ``` Use `-stdout` to print results while also writing `-out`. -Use `-verbose` to override stdout format while preserving the file format. +Use `-verbose` to override stdout format while preserving the +file format. ```bash # Write output in json format to results.json as well as stdout $ gosec -fmt=json -out=results.json -stdout *.go -# Overrides the output format to 'text' when stdout the results, while writing it to results.json +# Overrides the output format to 'text' when stdout the results, +# while writing it to results.json $ gosec -fmt=json -out=results.json -stdout -verbose=text *.go ``` -**Note:** gosec generates the [generic issue import format](https://docs.sonarqube.org/latest/analysis/generic-issue/) for SonarQube, and a report has to be imported into SonarQube using `sonar.externalIssuesReportPaths=path/to/gosec-report.json`. +**Note:** gosec generates the +[generic issue import format](https://docs.sonarqube.org/latest/analysis/generic-issue/) +for SonarQube, and a report has to be imported into SonarQube +using +`sonar.externalIssuesReportPaths=path/to/gosec-report.json`. ## Common usage patterns @@ -505,7 +658,8 @@ gosec -tests ./... ## Development -Development documentation was moved to [DEVELOPMENT.md](DEVELOPMENT.md). +Development documentation was moved to +[DEVELOPMENT.md](DEVELOPMENT.md). ## Who is using gosec? @@ -513,6 +667,7 @@ This is a [list](USERS.md) with some of the gosec's users. ## Sponsors -Support this project by becoming a sponsor. Your logo will show up here with a link to your website +Support this project by becoming a sponsor. Your logo will +show up here with a link to your website diff --git a/vendor/github.com/securego/gosec/v2/RULES.md b/vendor/github.com/securego/gosec/v2/RULES.md index 21994ce1..badc3b18 100644 --- a/vendor/github.com/securego/gosec/v2/RULES.md +++ b/vendor/github.com/securego/gosec/v2/RULES.md @@ -16,6 +16,7 @@ - [G104](#g104) - [G111](#g111) - [G117](#g117) + - [G118](#g118) - [G301, G302, G306, G307](#g301-g302-g306-g307) ## Rules List @@ -38,12 +39,13 @@ - G115 — Type conversion which leads to integer overflow (**SSA**) - G116 — Detect Trojan Source attacks using bidirectional Unicode characters (**AST**) - [G117](#g117) — Potential exposure of secrets via JSON/YAML/XML/TOML marshaling (**AST**) -- G118 — Context propagation failure leading to goroutine/resource leaks (**SSA**) +- [G118](#g118) — Context propagation failure leading to goroutine/resource leaks (**SSA**) - G119 — Unsafe redirect policy may propagate sensitive headers (**SSA**) -- G120 — Unbounded form parsing in HTTP handlers can cause memory exhaustion (**SSA**) +- G120 — Unbounded `ParseMultipartForm` in HTTP handlers can cause memory exhaustion (**Taint**) - G121 — Unsafe CrossOriginProtection bypass patterns (**SSA**) - G122 — Filesystem TOCTOU race risk in `filepath.Walk/WalkDir` callbacks (**SSA**) - G123 — TLS resumption may bypass `VerifyPeerCertificate` when `VerifyConnection` is unset (**SSA**) +- G124 — Insecure HTTP cookie configuration missing Secure, HttpOnly, or SameSite attributes (**SSA**) ### G2xx: Injection Patterns @@ -97,6 +99,9 @@ - G705 — XSS via taint analysis (**Taint**) - G706 — Log injection via taint analysis (**Taint**) - G707 — SMTP command/header injection via taint analysis (**Taint**) +- G708 — Server-side template injection via `text/template` (**Taint**) +- G709 — Unsafe deserialization of untrusted data (**Taint**) +- G710 — Open redirect via taint analysis (**Taint**) _Note: Implementation types used in this document:_ - **AST**: rule implemented in `rules/` and evaluated on AST patterns @@ -170,6 +175,106 @@ This replaces the default pattern. } ``` +### G118 + +`G118` detects three classes of context-propagation failure using SSA-level analysis: + +**1. Lost cancel function (CWE-400)** + +Reports when a `context.WithCancel`, `context.WithTimeout`, or `context.WithDeadline` call +returns a cancel function that is never called, potentially leaking resources. + +```go +// Flagged: cancel never called +func work(ctx context.Context) { + child, _ := context.WithTimeout(ctx, time.Second) + _ = child +} + +// Safe: cancel deferred +func work(ctx context.Context) { + child, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + _ = child +} +``` + +The following patterns are all recognised as *safe* (cancel is considered called): + +| Pattern | Description | +|---|---| +| `defer cancel()` | Direct deferred call | +| `defer func() { cancel() }()` | Cancel in a deferred closure | +| `cancelCopy := cancel; defer cancelCopy()` | Alias via variable | +| `return ctx, cancel` | Cancel returned to caller (responsibility transferred) | +| `s.cancelFn = cancel` + method `s.cancelFn()` | Stored in struct field, called via receiver method | +| `s.cancel = cancel; defer s.cancel()` | Stored in struct field, deferred in same function | +| `s.cancel = cancel; defer func() { s.cancel() }()` | Stored in struct field, called in closure | +| Struct containing field is returned | Caller inherits cancel responsibility | +| `var cancel CancelFunc` in `init()` + `cancel()` in another function | Package-level variable assigned in init, called in any function (e.g., signal handlers) | + +Example of package-level variable pattern: + +```go +// Safe: cancel stored in package-level variable and called in signal handler +var cancel context.CancelFunc + +func init() { + ctx, c := context.WithCancel(context.Background()) + cancel = c +} + +func handleShutdown() { + cancel() // Called from signal handler +} +``` + +**2. Goroutine uses `context.Background`/`TODO` when request context is available (CWE-400)** + +Reports when a goroutine spawned inside an HTTP handler or a function accepting a +`context.Context` / `*http.Request` uses `context.Background()` or `context.TODO()` +instead of the request-scoped context. + +```go +// Flagged +func handler(w http.ResponseWriter, r *http.Request) { + go func() { + ctx := context.Background() // ignores request context + doWork(ctx) + }() +} +``` + +**3. Long-running loop without `ctx.Done()` guard (CWE-400)** + +Reports an infinite loop that performs blocking I/O (e.g. `http.Get`, `db.Query`, +`time.Sleep`, interface methods such as `Read`/`Write`) but never checks `ctx.Done()`, +making the loop impossible to cancel. + +```go +// Flagged +func poll(ctx context.Context) { + for { + http.Get("https://example.com") // blocks, no cancellation path + time.Sleep(time.Second) + } +} + +// Safe +func poll(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case <-time.After(time.Second): + http.Get("https://example.com") + } + } +} +``` + +Loops with an external exit path (e.g. a `break` or bounded `for i < n`) are not flagged. + ### G301, G302, G306, G307 File and directory permission rules can be configured with stricter maximum permissions: diff --git a/vendor/github.com/securego/gosec/v2/action.yml b/vendor/github.com/securego/gosec/v2/action.yml index dce57794..62848c71 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://ghcr.io/securego/gosec:2.24.7" + image: "docker://ghcr.io/securego/gosec@sha256:d0859f242774504d57b17442b2dc77d4ffd2f4d30fbdf92c7eb612b2a394f477" # 2.27.1 args: - ${{ inputs.args }} diff --git a/vendor/github.com/securego/gosec/v2/analyzer.go b/vendor/github.com/securego/gosec/v2/analyzer.go index ed03efc6..0276cff8 100644 --- a/vendor/github.com/securego/gosec/v2/analyzer.go +++ b/vendor/github.com/securego/gosec/v2/analyzer.go @@ -29,6 +29,7 @@ import ( "path" "path/filepath" "reflect" + "regexp" "runtime/debug" "strconv" "strings" @@ -711,6 +712,9 @@ func (gosec *Analyzer) buildSSA(pkg *packages.Package) (*buildssa.SSA, error) { if pkg.TypesInfo == nil { return nil, fmt.Errorf("%w: %s", ErrNoPackageTypeInfo, pkg.Name) } + if pkg.IllTyped { + return nil, fmt.Errorf("package %s has type errors, skipping SSA analysis", pkg.Name) + } pass := &analysis.Pass{ Fset: pkg.Fset, Files: pkg.Syntax, @@ -758,20 +762,24 @@ func ParseErrors(pkg *packages.Package) (map[string][]Error, error) { return nil, nil } errs := make(map[string][]Error) + posRegexp := regexp.MustCompile(`^(.*?)(?::(\w+))?(?::(\w+))?$`) for _, pkgErr := range pkg.Errors { - parts := strings.Split(pkgErr.Pos, ":") - file := parts[0] + matches := posRegexp.FindStringSubmatch(pkgErr.Pos) + file := pkgErr.Pos var err error - var line int - if len(parts) > 1 { - if line, err = strconv.Atoi(parts[1]); err != nil { - return nil, fmt.Errorf("parsing line: %w", err) + var line, column int + if len(matches) > 0 { + file = matches[1] + file = strings.TrimSuffix(file, ":") + if len(matches) > 2 && matches[2] != "" { + if line, err = strconv.Atoi(matches[2]); err != nil { + return nil, fmt.Errorf("parsing line: %w", err) + } } - } - var column int - if len(parts) > 2 { - if column, err = strconv.Atoi(parts[2]); err != nil { - return nil, fmt.Errorf("parsing column: %w", err) + if len(matches) > 3 && matches[3] != "" { + if column, err = strconv.Atoi(matches[3]); err != nil { + return nil, fmt.Errorf("parsing column: %w", err) + } } } msg := strings.TrimSpace(pkgErr.Msg) @@ -797,6 +805,13 @@ func (gosec *Analyzer) AppendError(file string, err error) { gosec.errors[file] = errors } +// appendErrorAt appends an error tied to a specific source location. +func (gosec *Analyzer) appendErrorAt(file string, line, column int, err error) { + errs := gosec.errors[file] + errs = append(errs, *NewError(line, column, err.Error())) + gosec.errors[file] = errs +} + // findNoSecDirective checks if the comment group contains `#nosec` or `//gosec:disable` directive. // If found, it returns true and the directive's arguments. func findNoSecDirective(group *ast.CommentGroup, noSecDefaultTag, noSecAlternativeTag string) (bool, string) { @@ -960,6 +975,9 @@ func (v *astVisitor) ignore(n ast.Node) (map[string]issue.SuppressionInfo, *ast. noSecAlternativeTag = NoSecTag(noSecAlternativeTag) } + requireRules, _ := v.gosec.config.IsGlobalEnabled(NoSecRequireRules) + requireJustification, _ := v.gosec.config.IsGlobalEnabled(NoSecRequireJustification) + for _, group := range groups { found, args := findNoSecDirective(group, noSecDefaultTag, noSecAlternativeTag) if !found { @@ -968,49 +986,57 @@ func (v *astVisitor) ignore(n ast.Node) (map[string]issue.SuppressionInfo, *ast. v.stats.NumNosec++ justification := "" + hasJustificationDelim := false if idx := strings.Index(args, "--"); idx > -1 { + hasJustificationDelim = true justification = strings.TrimSpace(strings.TrimLeft(args[idx+2:], "-")) args = args[:idx] } directive := strings.TrimSpace(args) - // If the directive is empty or contains "block" (legacy), ignore all rules - if len(directive) == 0 || directive == "block" { - return map[string]issue.SuppressionInfo{ - aliasOfAllRules: { - Kind: "inSource", - Justification: justification, - }, - }, group - } - ignores := make(map[string]issue.SuppressionInfo) suppression := issue.SuppressionInfo{ Kind: "inSource", Justification: justification, } - // Manually parse identifiers starting with 'G' followed by 3 digits - for i := 0; i < len(directive); { - if directive[i] == 'G' && i+4 <= len(directive) { - ruleID := directive[i : i+4] - valid := true - for j := 1; j < 4; j++ { - if directive[i+j] < '0' || directive[i+j] > '9' { - valid = false - break + // Manually parse identifiers starting with 'G' followed by 3 digits. + // A directive that is empty or equals the legacy "block" keyword + // suppresses all rules. + if len(directive) != 0 && directive != "block" { + for i := 0; i < len(directive); { + if directive[i] == 'G' && i+4 <= len(directive) { + ruleID := directive[i : i+4] + valid := true + for j := 1; j < 4; j++ { + if directive[i+j] < '0' || directive[i+j] > '9' { + valid = false + break + } + } + if valid { + ignores[ruleID] = suppression + i += 4 + continue } } - if valid { - ignores[ruleID] = suppression - i += 4 - continue - } + i++ } - i++ } - if len(ignores) == 0 { + naked := len(ignores) == 0 + justificationMissing := !hasJustificationDelim || justification == "" + + if requireRules && naked { + v.reportInvalidDirective(group, "missing rule ID (e.g. G401); naked #nosec / //gosec:disable is disallowed by -nosec-require-rules") + continue + } + if requireJustification && justificationMissing { + v.reportInvalidDirective(group, "missing justification (expected `-- `); required by -nosec-require-justification") + continue + } + + if naked { ignores[aliasOfAllRules] = suppression } return ignores, group @@ -1018,6 +1044,18 @@ func (v *astVisitor) ignore(n ast.Node) (map[string]issue.SuppressionInfo, *ast. return nil, nil } +// reportInvalidDirective records an error for a malformed nosec directive so +// it surfaces in reports without suppressing any findings. +func (v *astVisitor) reportInvalidDirective(group *ast.CommentGroup, reason string) { + tokFile := v.context.FileSet.File(group.Pos()) + if tokFile == nil { + return + } + pos := tokFile.Position(group.Pos()) + v.gosec.appendErrorAt(tokFile.Name(), pos.Line, pos.Column, + fmt.Errorf("invalid nosec directive: %s", reason)) +} + // updateIssues updates the issues list with the given issue, handling suppressions. func (gosec *Analyzer) updateIssues(issue *issue.Issue, issues []*issue.Issue, stats *Metrics, allIgnores ignores) []*issue.Issue { if issue != nil { diff --git a/vendor/github.com/securego/gosec/v2/analyzers/analyzerslist.go b/vendor/github.com/securego/gosec/v2/analyzers/analyzerslist.go index 658b30a1..0a541e90 100644 --- a/vendor/github.com/securego/gosec/v2/analyzers/analyzerslist.go +++ b/vendor/github.com/securego/gosec/v2/analyzers/analyzerslist.go @@ -81,6 +81,34 @@ var ( Severity: "HIGH", CWE: "CWE-93", } + + SSTIRule = taint.RuleInfo{ + ID: "G708", + Description: "Server-side template injection via text/template", + Severity: "CRITICAL", + CWE: "CWE-94", + } + + UnsafeDeserializationRule = taint.RuleInfo{ + ID: "G709", + Description: "Unsafe deserialization of untrusted data", + Severity: "HIGH", + CWE: "CWE-502", + } + + OpenRedirectRule = taint.RuleInfo{ + ID: "G710", + Description: "Open redirect: user-controlled URL flows into http.Redirect", + Severity: "MEDIUM", + CWE: "CWE-601", + } + + FormParsingLimitRule = taint.RuleInfo{ + ID: "G120", + Description: "Unbounded multipart form parsing can cause memory exhaustion", + Severity: "MEDIUM", + CWE: "CWE-400", + } ) // AnalyzerList contains a mapping of analyzer ID's to analyzer definitions and a mapping @@ -128,6 +156,7 @@ var defaultAnalyzers = []AnalyzerDefinition{ {"G121", "Unsafe CrossOriginProtection bypass patterns", newCORSBypassPatternAnalyzer}, {"G122", "Filesystem TOCTOU race risk in filepath.Walk/WalkDir callbacks", newWalkSymlinkRaceAnalyzer}, {"G123", "TLS resumption may bypass VerifyPeerCertificate when VerifyConnection is unset", newTLSResumptionVerifyPeerAnalyzer}, + {"G124", "Insecure HTTP cookie configuration missing Secure, HttpOnly, or SameSite attributes", newInsecureCookieAnalyzer}, {"G602", "Possible slice bounds out of range", newSliceBoundsAnalyzer}, {"G407", "Use of hardcoded IV/nonce for encryption", newHardCodedNonce}, {"G408", "Stateful misuse of ssh.PublicKeyCallback leading to auth bypass", newSSHCallbackAnalyzer}, @@ -138,6 +167,9 @@ var defaultAnalyzers = []AnalyzerDefinition{ {"G705", "XSS via taint analysis", newXSSAnalyzer}, {"G706", "Log injection via taint analysis", newLogInjectionAnalyzer}, {"G707", "SMTP command/header injection via taint analysis", newSMTPInjectionAnalyzer}, + {"G708", "Server-side template injection via taint analysis", newSSTIAnalyzer}, + {"G709", "Unsafe deserialization of untrusted data via taint analysis", newUnsafeDeserializationAnalyzer}, + {"G710", "Open redirect via taint analysis", newOpenRedirectAnalyzer}, } // Generate the list of analyzers to use @@ -172,6 +204,10 @@ func DefaultTaintAnalyzers() []*analysis.Analyzer { xssConfig := XSS() logConfig := LogInjection() smtpConfig := SMTPInjection() + sstiConfig := SSTI() + deserConfig := UnsafeDeserialization() + formConfig := FormParsingLimits() + openRedirectConfig := OpenRedirect() return []*analysis.Analyzer{ taint.NewGosecAnalyzer(&SQLInjectionRule, &sqlConfig), @@ -181,5 +217,9 @@ func DefaultTaintAnalyzers() []*analysis.Analyzer { taint.NewGosecAnalyzer(&XSSRule, &xssConfig), taint.NewGosecAnalyzer(&LogInjectionRule, &logConfig), taint.NewGosecAnalyzer(&SMTPInjectionRule, &smtpConfig), + taint.NewGosecAnalyzer(&SSTIRule, &sstiConfig), + taint.NewGosecAnalyzer(&UnsafeDeserializationRule, &deserConfig), + taint.NewGosecAnalyzer(&FormParsingLimitRule, &formConfig), + taint.NewGosecAnalyzer(&OpenRedirectRule, &openRedirectConfig), } } diff --git a/vendor/github.com/securego/gosec/v2/analyzers/context_propagation.go b/vendor/github.com/securego/gosec/v2/analyzers/context_propagation.go index 0136d660..e5eee616 100644 --- a/vendor/github.com/securego/gosec/v2/analyzers/context_propagation.go +++ b/vendor/github.com/securego/gosec/v2/analyzers/context_propagation.go @@ -710,8 +710,40 @@ func isCancelCalled(cancelValue ssa.Value, allFuncs []*ssa.Function) bool { if isStructFieldReturnedFromFunc(fa) { return true } + // Check if any function (including closures capturing the + // struct) loads and calls the same field. This handles + // post-construction storage such as: + // s.cancel = cancel; defer s.cancel() + // s.cancel = cancel; defer func() { s.cancel() }() + if isFieldCalledInAnyFunc(fa, allFuncs) { + return true + } + } + // Check if storing to a package-level global variable. + // When cancel is stored to a global (e.g., in init()), we need + // to search all functions in the package for loads of that global + // followed by a call. + if global, ok := r.Addr.(*ssa.Global); ok { + if isGlobalCalledInAnyFunc(global, allFuncs) { + return true + } + } + // Cancel stored into a slice/array element (e.g. + // `defers = append(defers, cancel)`, which lowers to a Store + // into the variadic array). The cancel escapes into a + // collection whose iteration sites are not reliably traceable + // in SSA — treat as a transfer of responsibility, mirroring + // the global/returned-field handling above. + if _, ok := r.Addr.(*ssa.IndexAddr); ok { + return true } queue = append(queue, r.Addr) + case *ssa.MapUpdate: + // Cancel stored as a map value (e.g. + // `cleanups[key] = cancel`). Same reasoning as IndexAddr. + if r.Value == current { + return true + } case *ssa.UnOp: if r.Op == token.MUL && r.X == current { queue = append(queue, r) @@ -783,6 +815,163 @@ func isStructFieldReturnedFromFunc(fa *ssa.FieldAddr) bool { return false } +// isFieldCalledInAnyFunc checks whether a cancel function stored into a struct +// field is subsequently called in any function (including closures) that +// accesses the same field by struct pointer type and field index. This covers +// post-construction storage patterns not handled by isCancelCalledViaStructField: +// +// s.cancel = cancel; defer s.cancel() +// s.cancel = cancel; defer func() { s.cancel() }() +func isFieldCalledInAnyFunc(fa *ssa.FieldAddr, allFuncs []*ssa.Function) bool { + structPtrType := fa.X.Type() + fieldIdx := fa.Field + + for _, fn := range allFuncs { + if fn == nil { + continue + } + for _, block := range fn.Blocks { + for _, instr := range block.Instrs { + otherFA, ok := instr.(*ssa.FieldAddr) + if !ok || otherFA.Field != fieldIdx { + continue + } + if !types.Identical(otherFA.X.Type(), structPtrType) { + continue + } + if isFieldValueCalled(otherFA) { + return true + } + } + } + } + return false +} + +// isGlobalCalledInAnyFunc checks whether a cancel function stored into a +// package-level global variable is subsequently called in any function +// (including init(), main(), signal handlers, etc.). This handles patterns +// like: +// +// var cancel context.CancelFunc +// func init() { _, cancel = context.WithCancel(ctx) } +// func shutdown() { cancel() } +func isGlobalCalledInAnyFunc(global *ssa.Global, allFuncs []*ssa.Function) bool { + if global == nil { + return false + } + + // Iterate through all functions in the package to find loads from this global + for _, fn := range allFuncs { + if fn == nil || fn.Blocks == nil { + continue + } + + for _, block := range fn.Blocks { + for _, instr := range block.Instrs { + // Look for UnOp (dereference/load) from the global + unop, ok := instr.(*ssa.UnOp) + if !ok || unop.Op != token.MUL { + continue + } + + // Check if this load is from our global + if unop.X != global { + continue + } + + // Check if the loaded value is eventually called + if isValueCalled(unop) { + return true + } + } + } + } + + return false +} + +// isValueCalled checks if a value (typically a loaded function pointer) is +// eventually used as a callee. This performs a BFS through value referrers +// to find calls, handling phi nodes, stores/loads, type conversions, and closures. +func isValueCalled(value ssa.Value) bool { + if value == nil { + return false + } + + refs := value.Referrers() + if refs == nil { + return false + } + + queue := []ssa.Value{value} + visited := make(map[ssa.Value]bool) + + for len(queue) > 0 { + cur := queue[0] + queue = queue[1:] + + if cur == nil || visited[cur] { + continue + } + visited[cur] = true + + curRefs := cur.Referrers() + if curRefs == nil { + continue + } + + for _, ref := range *curRefs { + switch r := ref.(type) { + case ssa.CallInstruction: + // Check if cur is used as the callee or an argument + if isUsedInCall(r.Common(), cur) { + return true + } + case *ssa.Phi: + // Value flows through phi node - continue tracking + queue = append(queue, r) + case *ssa.Store: + // Stored then loaded elsewhere - follow the address + if r.Val == cur { + queue = append(queue, r.Addr) + } + case *ssa.UnOp: + // Dereference or other operation - continue tracking + if r.X == cur { + queue = append(queue, r) + } + case *ssa.ChangeType: + // Type conversion - continue tracking + if r.X == cur { + queue = append(queue, r) + } + case *ssa.Convert: + // Type conversion - continue tracking + if r.X == cur { + queue = append(queue, r) + } + case *ssa.MakeInterface: + // Wrapped in interface - continue tracking + if r.X == cur { + queue = append(queue, r) + } + case *ssa.MakeClosure: + // Captured in closure - follow into closure body + if fn, ok := r.Fn.(*ssa.Function); ok { + for i, binding := range r.Bindings { + if binding == cur && i < len(fn.FreeVars) { + queue = append(queue, fn.FreeVars[i]) + } + } + } + } + } + } + + return false +} + // isCancelCalledViaStructField checks whether a cancel function stored into a // struct field (e.g., job.cancelFn = cancel) is subsequently called in any other // method of the same receiver type (e.g., job.Close() calls job.cancelFn()). diff --git a/vendor/github.com/securego/gosec/v2/analyzers/conversion_overflow.go b/vendor/github.com/securego/gosec/v2/analyzers/conversion_overflow.go index 23a350b4..2c3c7d86 100644 --- a/vendor/github.com/securego/gosec/v2/analyzers/conversion_overflow.go +++ b/vendor/github.com/securego/gosec/v2/analyzers/conversion_overflow.go @@ -79,6 +79,13 @@ func runConversionOverflow(pass *analysis.Pass) (any, error) { continue } + // Skip conversions between platform-word-sized + // types (e.g. uintptr -> int) since they never + // truncate bits. + if isSameWidthPlatformConversion(instr.X.Type(), instr.Type()) { + continue + } + if hasOverflow(srcInfo, dstInfo) { if state.isSafeConversion(instr, dstInfo) { continue @@ -140,6 +147,28 @@ func hasOverflow(srcInfo, dstInfo IntTypeInfo) bool { return srcInfo.Min < dstInfo.Min || srcInfo.Max > dstInfo.Max } +// isSameWidthPlatformConversion returns true when both the source +// and destination are platform-word-sized integer types (e.g. +// uintptr -> int). These conversions never truncate bits because +// Go guarantees both types have the same width on every platform. +func isSameWidthPlatformConversion(src, dst types.Type) bool { + srcBasic, _ := src.Underlying().(*types.Basic) + dstBasic, _ := dst.Underlying().(*types.Basic) + if srcBasic == nil || dstBasic == nil { + return false + } + return isPlatformWordType(srcBasic.Kind()) && + isPlatformWordType(dstBasic.Kind()) +} + +func isPlatformWordType(k types.BasicKind) bool { + switch k { + case types.Int, types.Uint, types.Uintptr: + return true + } + return false +} + // hasRangeCheck determines if there is a valid range check for the given value that ensures safety. func (s *overflowState) hasRangeCheck(v ssa.Value, dstInt IntTypeInfo, block *ssa.BasicBlock) bool { // Clear visited map for new resolution diff --git a/vendor/github.com/securego/gosec/v2/analyzers/dependency_checker.go b/vendor/github.com/securego/gosec/v2/analyzers/dependency_checker.go new file mode 100644 index 00000000..2f800ab2 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/analyzers/dependency_checker.go @@ -0,0 +1,116 @@ +// (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/ssa" + +type dependencyKey struct { + value ssa.Value + target ssa.Value +} + +type dependencyChecker struct { + memo map[dependencyKey]bool + visiting map[dependencyKey]struct{} +} + +func newDependencyChecker() *dependencyChecker { + return &dependencyChecker{ + memo: make(map[dependencyKey]bool), + visiting: make(map[dependencyKey]struct{}), + } +} + +func (c *dependencyChecker) dependsOn(value ssa.Value, target ssa.Value) bool { + return c.dependsOnDepth(value, target, 0) +} + +func (c *dependencyChecker) dependsOnDepth(value ssa.Value, target ssa.Value, depth int) bool { + if value == nil || target == nil || depth > MaxDepth { + return false + } + if value == target { + return true + } + + key := dependencyKey{value: value, target: target} + if result, ok := c.memo[key]; ok { + return result + } + if _, ok := c.visiting[key]; ok { + return false + } + + c.visiting[key] = struct{}{} + result := false + + switch v := value.(type) { + case *ssa.ChangeType: + result = c.dependsOnDepth(v.X, target, depth+1) + case *ssa.MakeInterface: + result = c.dependsOnDepth(v.X, target, depth+1) + case *ssa.TypeAssert: + result = c.dependsOnDepth(v.X, target, depth+1) + case *ssa.UnOp: + result = c.dependsOnDepth(v.X, target, depth+1) + case *ssa.FieldAddr: + result = c.dependsOnDepth(v.X, target, depth+1) + case *ssa.Field: + result = c.dependsOnDepth(v.X, target, depth+1) + case *ssa.IndexAddr: + result = c.dependsOnDepth(v.X, target, depth+1) || c.dependsOnDepth(v.Index, target, depth+1) + case *ssa.Index: + result = c.dependsOnDepth(v.X, target, depth+1) || c.dependsOnDepth(v.Index, target, depth+1) + case *ssa.Slice: + if c.dependsOnDepth(v.X, target, depth+1) { + result = true + break + } + if v.Low != nil && c.dependsOnDepth(v.Low, target, depth+1) { + result = true + break + } + if v.High != nil && c.dependsOnDepth(v.High, target, depth+1) { + result = true + break + } + result = v.Max != nil && c.dependsOnDepth(v.Max, target, depth+1) + case *ssa.Extract: + result = c.dependsOnDepth(v.Tuple, target, depth+1) + case *ssa.Phi: + for _, edge := range v.Edges { + if c.dependsOnDepth(edge, target, depth+1) { + result = true + break + } + } + case *ssa.Call: + if v.Call.Value != nil && c.dependsOnDepth(v.Call.Value, target, depth+1) { + result = true + break + } + for _, arg := range v.Call.Args { + if c.dependsOnDepth(arg, target, depth+1) { + result = true + break + } + } + } + + delete(c.visiting, key) + c.memo[key] = result + + return result +} diff --git a/vendor/github.com/securego/gosec/v2/analyzers/form_parsing_limits.go b/vendor/github.com/securego/gosec/v2/analyzers/form_parsing_limits.go index 0c4723f0..8b27d6d1 100644 --- a/vendor/github.com/securego/gosec/v2/analyzers/form_parsing_limits.go +++ b/vendor/github.com/securego/gosec/v2/analyzers/form_parsing_limits.go @@ -15,507 +15,38 @@ package analyzers import ( - "go/token" - "go/types" - "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/internal/ssautil" - "github.com/securego/gosec/v2/issue" + "github.com/securego/gosec/v2/taint" ) -const msgUnboundedFormParsing = "Parsing form data without limiting request body size can allow memory exhaustion (use http.MaxBytesReader)" - -type dependencyKey struct { - value ssa.Value - target ssa.Value -} - -type dependencyChecker struct { - memo map[dependencyKey]bool - visiting map[dependencyKey]struct{} -} - -func newDependencyChecker() *dependencyChecker { - return &dependencyChecker{ - memo: make(map[dependencyKey]bool), - visiting: make(map[dependencyKey]struct{}), - } -} - -func (c *dependencyChecker) dependsOn(value ssa.Value, target ssa.Value) bool { - return c.dependsOnDepth(value, target, 0) -} - -func (c *dependencyChecker) dependsOnDepth(value ssa.Value, target ssa.Value, depth int) bool { - if value == nil || target == nil || depth > MaxDepth { - return false - } - if value == target { - return true - } - - key := dependencyKey{value: value, target: target} - if result, ok := c.memo[key]; ok { - return result - } - if _, ok := c.visiting[key]; ok { - return false - } - - c.visiting[key] = struct{}{} - result := false - - switch v := value.(type) { - case *ssa.ChangeType: - result = c.dependsOnDepth(v.X, target, depth+1) - case *ssa.MakeInterface: - result = c.dependsOnDepth(v.X, target, depth+1) - case *ssa.TypeAssert: - result = c.dependsOnDepth(v.X, target, depth+1) - case *ssa.UnOp: - result = c.dependsOnDepth(v.X, target, depth+1) - case *ssa.FieldAddr: - result = c.dependsOnDepth(v.X, target, depth+1) - case *ssa.Field: - result = c.dependsOnDepth(v.X, target, depth+1) - case *ssa.IndexAddr: - result = c.dependsOnDepth(v.X, target, depth+1) || c.dependsOnDepth(v.Index, target, depth+1) - case *ssa.Index: - result = c.dependsOnDepth(v.X, target, depth+1) || c.dependsOnDepth(v.Index, target, depth+1) - case *ssa.Slice: - if c.dependsOnDepth(v.X, target, depth+1) { - result = true - break - } - if v.Low != nil && c.dependsOnDepth(v.Low, target, depth+1) { - result = true - break - } - if v.High != nil && c.dependsOnDepth(v.High, target, depth+1) { - result = true - break - } - result = v.Max != nil && c.dependsOnDepth(v.Max, target, depth+1) - case *ssa.Extract: - result = c.dependsOnDepth(v.Tuple, target, depth+1) - case *ssa.Phi: - for _, edge := range v.Edges { - if c.dependsOnDepth(edge, target, depth+1) { - result = true - break - } - } - case *ssa.Call: - if v.Call.Value != nil && c.dependsOnDepth(v.Call.Value, target, depth+1) { - result = true - break - } - for _, arg := range v.Call.Args { - if c.dependsOnDepth(arg, target, depth+1) { - result = true - break - } - } +// FormParsingLimits returns a taint analysis configuration for detecting +// unbounded multipart form parsing in HTTP handlers. +// +// Only ParseMultipartForm is flagged because ParseForm, FormValue, and +// PostFormValue already enforce a built-in 10 MiB body limit in Go's +// standard library (see net/http.Request.ParseForm documentation). +func FormParsingLimits() taint.Config { + return taint.Config{ + Sources: []taint.Source{ + {Package: "net/http", Name: "Request", Pointer: true}, + }, + Sinks: []taint.Sink{ + // ParseMultipartForm reads the entire body into memory/disk with + // no automatic cap — the caller-supplied maxMemory only limits the + // in-memory portion while the total can be maxMemory + 10 MiB. + // Without http.MaxBytesReader the full body is consumed. + // CheckArgs: [0] checks only the receiver (*http.Request). + {Package: "net/http", Receiver: "Request", Method: "ParseMultipartForm", Pointer: true, CheckArgs: []int{0}}, + }, + Sanitizers: []taint.Sanitizer{}, } - - delete(c.visiting, key) - c.memo[key] = result - - return result } func newFormParsingLimitAnalyzer(id string, description string) *analysis.Analyzer { - return &analysis.Analyzer{ - Name: id, - Doc: description, - Run: runFormParsingLimitAnalysis, - Requires: []*analysis.Analyzer{buildssa.Analyzer}, - } -} - -func runFormParsingLimitAnalysis(pass *analysis.Pass) (any, error) { - ssaResult, err := ssautil.GetSSAResult(pass) - if err != nil { - return nil, err - } - - checker := newDependencyChecker() - issuesByPos := make(map[token.Pos]*issue.Issue) - handlerProtection := computeFormParsingHandlerProtection(ssaResult.SSA.SrcFuncs, checker) - - for _, fn := range collectAnalyzerFunctions(ssaResult.SSA.SrcFuncs) { - requestParam, writerParam := findHandlerRequestAndWriterParams(fn) - if requestParam == nil || writerParam == nil { - continue - } - - hasRequestBodyLimit := handlerProtection[fn] - if hasRequestBodyLimit { - continue - } - - for _, block := range fn.Blocks { - for _, instr := range block.Instrs { - callInstr, ok := instr.(ssa.CallInstruction) - if !ok { - continue - } - if !isRiskyFormParsingCall(callInstr, requestParam, checker) { - continue - } - addRedirectIssue(issuesByPos, pass, instr.Pos(), msgUnboundedFormParsing, issue.Medium, issue.High) - } - } - } - - if len(issuesByPos) == 0 { - return nil, nil - } - - issues := make([]*issue.Issue, 0, len(issuesByPos)) - for _, i := range issuesByPos { - issues = append(issues, i) - } - - return issues, nil -} - -func computeFormParsingHandlerProtection(srcFuncs []*ssa.Function, checker *dependencyChecker) map[*ssa.Function]bool { - protection := make(map[*ssa.Function]bool) - allFuncs := collectAnalyzerFunctions(srcFuncs) - for _, fn := range allFuncs { - requestParam, writerParam := findHandlerRequestAndWriterParams(fn) - if requestParam == nil || writerParam == nil { - continue - } - if functionHasRequestBodyLimit(fn, requestParam, writerParam, checker) { - protection[fn] = true - continue - } - if isProtectedByWrapperCall(fn, allFuncs, checker) { - protection[fn] = true - } - } - - return protection -} - -func isProtectedByWrapperCall(handler *ssa.Function, allFuncs []*ssa.Function, checker *dependencyChecker) bool { - for _, fn := range allFuncs { - if fn == nil { - continue - } - for _, block := range fn.Blocks { - for _, instr := range block.Instrs { - callInstr, ok := instr.(ssa.CallInstruction) - if !ok { - continue - } - common := callInstr.Common() - if common == nil { - continue - } - wrapper := common.StaticCallee() - if wrapper == nil { - continue - } - - for argIndex, arg := range common.Args { - if !checker.dependsOn(arg, handler) { - continue - } - if wrapperProtectsParamHandler(wrapper, argIndex, checker) { - return true - } - } - } - } - } - - return false -} - -func wrapperProtectsParamHandler(wrapper *ssa.Function, paramIndex int, checker *dependencyChecker) bool { - if wrapper == nil || paramIndex < 0 || paramIndex >= len(wrapper.Params) { - return false - } - handlerParam := wrapper.Params[paramIndex] - - if wrapperDelegatesWithRequestLimit(wrapper, handlerParam, checker) { - return true - } - - for _, block := range wrapper.Blocks { - for _, instr := range block.Instrs { - makeClosure, ok := instr.(*ssa.MakeClosure) - if !ok { - continue - } - closureFn, ok := makeClosure.Fn.(*ssa.Function) - if !ok || closureFn == nil { - continue - } - - requestParam, writerParam := findHandlerRequestAndWriterParams(closureFn) - if requestParam == nil || writerParam == nil { - continue - } - if !functionHasRequestBodyLimit(closureFn, requestParam, writerParam, checker) { - continue - } - - for bindingIndex, binding := range makeClosure.Bindings { - if !bindingDependsOnValue(binding, handlerParam, checker) { - continue - } - if closureDelegatesWithRequestLimit(closureFn, bindingIndex, requestParam, writerParam, checker) { - return true - } - } - } - } - - return false -} - -func bindingDependsOnValue(binding ssa.Value, target ssa.Value, checker *dependencyChecker) bool { - if checker.dependsOn(binding, target) { - return true - } - - alloc, ok := binding.(*ssa.Alloc) - if !ok { - return false - } - - for _, ref := range safeReferrers(alloc) { - store, ok := ref.(*ssa.Store) - if !ok { - continue - } - if store.Addr != alloc { - continue - } - if checker.dependsOn(store.Val, target) { - return true - } - } - - return false -} - -func wrapperDelegatesWithRequestLimit(wrapper *ssa.Function, handlerValue ssa.Value, checker *dependencyChecker) bool { - requestParam, writerParam := findHandlerRequestAndWriterParams(wrapper) - if requestParam == nil || writerParam == nil { - return false - } - if !functionHasRequestBodyLimit(wrapper, requestParam, writerParam, checker) { - return false - } - return hasServeHTTPDelegation(wrapper, handlerValue, writerParam, requestParam, checker) -} - -func closureDelegatesWithRequestLimit(closure *ssa.Function, freeVarIndex int, requestParam *ssa.Parameter, writerParam *ssa.Parameter, checker *dependencyChecker) bool { - if freeVarIndex < 0 || freeVarIndex >= len(closure.FreeVars) { - return false - } - handlerValue := closure.FreeVars[freeVarIndex] - return hasServeHTTPDelegation(closure, handlerValue, writerParam, requestParam, checker) -} - -func hasServeHTTPDelegation(fn *ssa.Function, handlerValue ssa.Value, writerValue ssa.Value, requestValue ssa.Value, checker *dependencyChecker) bool { - for _, block := range fn.Blocks { - for _, instr := range block.Instrs { - call, ok := instr.(*ssa.Call) - if !ok { - continue - } - common := call.Common() - if common == nil { - continue - } - - var ( - receiver ssa.Value - writer ssa.Value - request ssa.Value - ) - - if method := common.Method; method != nil && method.Name() == "ServeHTTP" { - if len(common.Args) < 2 { - continue - } - receiver = common.Value - writer = common.Args[0] - request = common.Args[1] - } else { - callee := common.StaticCallee() - if callee == nil || callee.Name() != "ServeHTTP" || callee.Signature == nil || callee.Signature.Recv() == nil { - continue - } - if len(common.Args) < 3 { - continue - } - receiver = common.Args[0] - writer = common.Args[1] - request = common.Args[2] - } - - if !checker.dependsOn(receiver, handlerValue) { - continue - } - if !checker.dependsOn(writer, writerValue) { - continue - } - if !checker.dependsOn(request, requestValue) { - continue - } - return true - } - } - - return false -} - -func findHandlerRequestAndWriterParams(fn *ssa.Function) (*ssa.Parameter, *ssa.Parameter) { - if fn == nil { - return nil, nil - } - - var requestParam *ssa.Parameter - var writerParam *ssa.Parameter - - for _, param := range fn.Params { - if param == nil { - continue - } - if requestParam == nil && isHTTPRequestPointerType(param.Type()) { - requestParam = param - continue - } - if writerParam == nil && isHTTPResponseWriterType(param.Type()) { - writerParam = param - } - } - - return requestParam, writerParam -} - -func isHTTPResponseWriterType(t types.Type) bool { - named, ok := t.(*types.Named) - if !ok { - return false - } - - obj := named.Obj() - if obj == nil || obj.Name() != "ResponseWriter" { - return false - } - - pkg := obj.Pkg() - return pkg != nil && pkg.Path() == "net/http" -} - -func functionHasRequestBodyLimit(fn *ssa.Function, requestParam *ssa.Parameter, writerParam *ssa.Parameter, checker *dependencyChecker) bool { - for _, block := range fn.Blocks { - for _, instr := range block.Instrs { - store, ok := instr.(*ssa.Store) - if !ok { - continue - } - if isRequestBodyStoreFromMaxBytesReader(store, requestParam, writerParam, checker) { - return true - } - } - } - return false -} - -func isRequestBodyStoreFromMaxBytesReader(store *ssa.Store, requestParam *ssa.Parameter, writerParam *ssa.Parameter, checker *dependencyChecker) bool { - fieldAddr, ok := store.Addr.(*ssa.FieldAddr) - if !ok { - return false - } - - if !checker.dependsOn(fieldAddr.X, requestParam) { - return false - } - - if !isMaxBytesReaderValue(store.Val, requestParam, writerParam, checker, 0) { - return false - } - - return true -} - -func isMaxBytesReaderValue(v ssa.Value, requestParam *ssa.Parameter, writerParam *ssa.Parameter, checker *dependencyChecker, depth int) bool { - if v == nil || depth > MaxDepth { - return false - } - - switch value := v.(type) { - case *ssa.Call: - callee := value.Call.StaticCallee() - if callee == nil || callee.Name() != "MaxBytesReader" { - return false - } - if callee.Pkg == nil || callee.Pkg.Pkg == nil || callee.Pkg.Pkg.Path() != "net/http" { - return false - } - if len(value.Call.Args) < 3 { - return false - } - if !checker.dependsOn(value.Call.Args[0], writerParam) { - return false - } - return checker.dependsOn(value.Call.Args[1], requestParam) - case *ssa.ChangeType: - return isMaxBytesReaderValue(value.X, requestParam, writerParam, checker, depth+1) - case *ssa.MakeInterface: - return isMaxBytesReaderValue(value.X, requestParam, writerParam, checker, depth+1) - case *ssa.TypeAssert: - return isMaxBytesReaderValue(value.X, requestParam, writerParam, checker, depth+1) - case *ssa.Phi: - for _, edge := range value.Edges { - if isMaxBytesReaderValue(edge, requestParam, writerParam, checker, depth+1) { - return true - } - } - } - - return false -} - -func isRiskyFormParsingCall(callInstr ssa.CallInstruction, requestParam *ssa.Parameter, checker *dependencyChecker) bool { - common := callInstr.Common() - if common == nil { - return false - } - - callee := common.StaticCallee() - if callee == nil { - return false - } - - if callee.Signature == nil || callee.Signature.Recv() == nil { - return false - } - - if !isHTTPRequestPointerType(callee.Signature.Recv().Type()) { - return false - } - - name := callee.Name() - if name != "ParseForm" && name != "ParseMultipartForm" && name != "FormValue" && name != "PostFormValue" { - return false - } - - if len(common.Args) == 0 { - return false - } - - return checker.dependsOn(common.Args[0], requestParam) + config := FormParsingLimits() + rule := FormParsingLimitRule + rule.ID = id + rule.Description = description + return taint.NewGosecAnalyzer(&rule, &config) } diff --git a/vendor/github.com/securego/gosec/v2/analyzers/insecure_cookie.go b/vendor/github.com/securego/gosec/v2/analyzers/insecure_cookie.go new file mode 100644 index 00000000..5c22eeb6 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/analyzers/insecure_cookie.go @@ -0,0 +1,269 @@ +// (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 ( + "go/constant" + "go/token" + "go/types" + + "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/internal/ssautil" + "github.com/securego/gosec/v2/issue" +) + +func newInsecureCookieAnalyzer(id string, description string) *analysis.Analyzer { + return &analysis.Analyzer{ + Name: id, + Doc: description, + Run: runInsecureCookieAnalysis, + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + } +} + +// cookieState tracks the security-relevant fields set on a single http.Cookie allocation. +type cookieState struct { + allocPos token.Pos + secureSet bool + httpOnlySet bool + sameSiteSet bool + // Track the actual values when explicitly set + secureTrue bool + httpOnlyTrue bool + sameSiteSafe bool // SameSiteStrictMode (3) or SameSiteLaxMode (2) +} + +type insecureCookieState struct { + *BaseAnalyzerState + cookies map[ssa.Value]*cookieState + issuesByPos map[token.Pos]*issue.Issue +} + +func newInsecureCookieState(pass *analysis.Pass) *insecureCookieState { + return &insecureCookieState{ + BaseAnalyzerState: NewBaseState(pass), + cookies: make(map[ssa.Value]*cookieState), + issuesByPos: make(map[token.Pos]*issue.Issue), + } +} + +func runInsecureCookieAnalysis(pass *analysis.Pass) (any, error) { + ssaResult, err := ssautil.GetSSAResult(pass) + if err != nil { + return nil, err + } + + state := newInsecureCookieState(pass) + defer state.Release() + + funcs := collectAnalyzerFunctions(ssaResult.SSA.SrcFuncs) + if len(funcs) == 0 { + return nil, nil + } + + // Phase 1: Collect field stores on http.Cookie allocations. + TraverseSSA(funcs, func(_ *ssa.BasicBlock, instr ssa.Instruction) { + store, ok := instr.(*ssa.Store) + if !ok { + return + } + state.trackCookieFieldStore(store) + }) + + // Phase 2: Report cookies missing secure attributes. + state.reportInsecureCookies() + + if len(state.issuesByPos) == 0 { + return nil, nil + } + + issues := make([]*issue.Issue, 0, len(state.issuesByPos)) + for _, i := range state.issuesByPos { + issues = append(issues, i) + } + return issues, nil +} + +func (s *insecureCookieState) trackCookieFieldStore(store *ssa.Store) { + fieldAddr, ok := store.Addr.(*ssa.FieldAddr) + if !ok { + return + } + + if !isHTTPCookiePointerType(fieldAddr.X.Type()) { + return + } + + fieldName, ok := httpCookieFieldName(fieldAddr) + if !ok { + return + } + + root := cookieRoot(fieldAddr.X, 0) + if root == nil { + return + } + + cs := s.getOrCreateCookieState(root) + + switch fieldName { + case "Secure": + cs.secureSet = true + if b, ok := boolConstValue(store.Val); ok { + cs.secureTrue = b + } + case "HttpOnly": + cs.httpOnlySet = true + if b, ok := boolConstValue(store.Val); ok { + cs.httpOnlyTrue = b + } + case "SameSite": + cs.sameSiteSet = true + if c, ok := store.Val.(*ssa.Const); ok && c.Value != nil { + // http.SameSiteLaxMode = 2, http.SameSiteStrictMode = 3 + val, isInt := intConstValue(c) + if isInt && (val == 2 || val == 3) { + cs.sameSiteSafe = true + } + } + } +} + +func (s *insecureCookieState) getOrCreateCookieState(root ssa.Value) *cookieState { + if cs, ok := s.cookies[root]; ok { + return cs + } + cs := &cookieState{allocPos: root.Pos()} + s.cookies[root] = cs + return cs +} + +func (s *insecureCookieState) reportInsecureCookies() { + for _, cs := range s.cookies { + if cs.allocPos == token.NoPos { + continue + } + + // Check: Secure must be explicitly set to true + if !cs.secureSet || !cs.secureTrue { + s.addIssue(cs.allocPos, "http.Cookie missing or has insecure Secure, HttpOnly, or SameSite attribute") + continue + } + // Check: HttpOnly must be explicitly set to true + if !cs.httpOnlySet || !cs.httpOnlyTrue { + s.addIssue(cs.allocPos, "http.Cookie missing or has insecure Secure, HttpOnly, or SameSite attribute") + continue + } + // Check: SameSite must be Lax or Strict + if !cs.sameSiteSet || !cs.sameSiteSafe { + s.addIssue(cs.allocPos, "http.Cookie missing or has insecure Secure, HttpOnly, or SameSite attribute") + continue + } + } +} + +func (s *insecureCookieState) addIssue(pos token.Pos, msg string) { + if pos == token.NoPos { + return + } + if _, exists := s.issuesByPos[pos]; exists { + return + } + s.issuesByPos[pos] = newIssue(s.Pass.Analyzer.Name, msg, s.Pass.Fset, pos, issue.Medium, issue.High) +} + +// isHTTPCookiePointerType returns true if t is *net/http.Cookie. +func isHTTPCookiePointerType(t types.Type) bool { + ptr, ok := t.(*types.Pointer) + if !ok { + return false + } + named, ok := ptr.Elem().(*types.Named) + if !ok { + return false + } + obj := named.Obj() + if obj == nil || obj.Name() != "Cookie" { + return false + } + pkg := obj.Pkg() + return pkg != nil && pkg.Path() == "net/http" +} + +// httpCookieFieldName returns the field name for a FieldAddr on *http.Cookie. +func httpCookieFieldName(fieldAddr *ssa.FieldAddr) (string, bool) { + if fieldAddr == nil { + return "", false + } + t := fieldAddr.X.Type() + if ptr, ok := t.(*types.Pointer); ok { + t = ptr.Elem() + } + named, ok := t.(*types.Named) + if !ok { + return "", false + } + if named.Obj() == nil || named.Obj().Pkg() == nil || + named.Obj().Pkg().Path() != "net/http" || named.Obj().Name() != "Cookie" { + return "", false + } + st, ok := named.Underlying().(*types.Struct) + if !ok || fieldAddr.Field >= st.NumFields() { + return "", false + } + return st.Field(fieldAddr.Field).Name(), true +} + +// cookieRoot traces a value back to its http.Cookie allocation root. +func cookieRoot(v ssa.Value, depth int) ssa.Value { + if v == nil || depth > MaxDepth { + return nil + } + if isHTTPCookiePointerType(v.Type()) { + return v + } + switch value := v.(type) { + case *ssa.ChangeType: + return cookieRoot(value.X, depth+1) + case *ssa.MakeInterface: + return cookieRoot(value.X, depth+1) + case *ssa.TypeAssert: + return cookieRoot(value.X, depth+1) + case *ssa.UnOp: + return cookieRoot(value.X, depth+1) + case *ssa.FieldAddr: + return cookieRoot(value.X, depth+1) + case *ssa.Phi: + if len(value.Edges) > 0 { + return cookieRoot(value.Edges[0], depth+1) + } + } + return nil +} + +// intConstValue extracts an int64 from an ssa.Const. +func intConstValue(c *ssa.Const) (int64, bool) { + if c == nil || c.Value == nil { + return 0, false + } + if c.Value.Kind() != constant.Int { + return 0, false + } + val, ok := constant.Int64Val(c.Value) + return val, ok +} diff --git a/vendor/github.com/securego/gosec/v2/analyzers/loginjection.go b/vendor/github.com/securego/gosec/v2/analyzers/loginjection.go index 2cbd1135..a6abe2f9 100644 --- a/vendor/github.com/securego/gosec/v2/analyzers/loginjection.go +++ b/vendor/github.com/securego/gosec/v2/analyzers/loginjection.go @@ -46,10 +46,19 @@ func LogInjection() taint.Config { {Package: "log", Method: "Panic"}, {Package: "log", Method: "Panicf"}, {Package: "log", Method: "Panicln"}, - {Package: "log/slog", Method: "Info"}, - {Package: "log/slog", Method: "Warn"}, - {Package: "log/slog", Method: "Error"}, - {Package: "log/slog", Method: "Debug"}, + // log/slog structured logging functions have the signature: + // func Warn(msg string, args ...any) + // The variadic `args` are key-value attribute pairs whose values are + // automatically escaped by both TextHandler (JSON-quoted) and JSONHandler + // (JSON-encoded), making them safe against log injection. + // Only the `msg` argument (args[0]) is a real injection vector because + // TextHandler writes it verbatim without quoting. + // CheckArgs: []int{0} scopes the taint check to the message only, + // preventing false positives on: slog.Warn("msg", "key", taintedVal) + {Package: "log/slog", Method: "Info", CheckArgs: []int{0}}, + {Package: "log/slog", Method: "Warn", CheckArgs: []int{0}}, + {Package: "log/slog", Method: "Error", CheckArgs: []int{0}}, + {Package: "log/slog", Method: "Debug", CheckArgs: []int{0}}, }, Sanitizers: []taint.Sanitizer{ // strings.ReplaceAll can strip newlines/CRLF for log injection diff --git a/vendor/github.com/securego/gosec/v2/analyzers/openredirect.go b/vendor/github.com/securego/gosec/v2/analyzers/openredirect.go new file mode 100644 index 00000000..bcdddc96 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/analyzers/openredirect.go @@ -0,0 +1,67 @@ +// (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" + + "github.com/securego/gosec/v2/taint" +) + +// OpenRedirect returns a configuration for detecting open-redirect vulnerabilities +// where user-controlled data flows into the URL argument of net/http.Redirect. +// See CWE-601. +func OpenRedirect() taint.Config { + return taint.Config{ + Sources: []taint.Source{ + // Type sources: tainted when received as parameters from external callers. + // Any read from a *http.Request (FormValue, URL.Query().Get, Cookie, etc.) + // propagates taint through the existing receiver-based logic in isTainted. + {Package: "net/http", Name: "Request", Pointer: true}, + {Package: "net/url", Name: "URL", Pointer: true}, + {Package: "net/url", Name: "Values"}, + }, + Sinks: []taint.Sink{ + // http.Redirect(w, r, url, code): only the URL string (arg index 2) + // is the redirect target. Skipping arg 1 (*http.Request) prevents the + // receiver itself from being treated as a tainted sink argument. + {Package: "net/http", Method: "Redirect", CheckArgs: []int{2}}, + }, + Sanitizers: []taint.Sanitizer{ + // url.PathEscape / QueryEscape neutralize untrusted path or query + // fragments embedded into a hard-coded base URL. + {Package: "net/url", Method: "PathEscape"}, + {Package: "net/url", Method: "QueryEscape"}, + + // Numeric conversions cannot produce a URL host or scheme. + {Package: "strconv", Method: "Atoi"}, + {Package: "strconv", Method: "Itoa"}, + {Package: "strconv", Method: "ParseInt"}, + {Package: "strconv", Method: "ParseUint"}, + {Package: "strconv", Method: "FormatInt"}, + {Package: "strconv", Method: "FormatUint"}, + }, + } +} + +// newOpenRedirectAnalyzer creates an analyzer for detecting open-redirect +// vulnerabilities via taint analysis (G710). +func newOpenRedirectAnalyzer(id string, description string) *analysis.Analyzer { + config := OpenRedirect() + rule := OpenRedirectRule + rule.ID = id + rule.Description = description + return taint.NewGosecAnalyzer(&rule, &config) +} diff --git a/vendor/github.com/securego/gosec/v2/analyzers/pathtraversal.go b/vendor/github.com/securego/gosec/v2/analyzers/pathtraversal.go index 34142982..ba9b72e8 100644 --- a/vendor/github.com/securego/gosec/v2/analyzers/pathtraversal.go +++ b/vendor/github.com/securego/gosec/v2/analyzers/pathtraversal.go @@ -61,10 +61,15 @@ func PathTraversal() taint.Config { {Package: "io/ioutil", Method: "ReadDir"}, {Package: "path/filepath", Method: "Walk"}, {Package: "path/filepath", Method: "WalkDir"}, + // HTTP file-serving functions: user-controlled path = arbitrary file read + {Package: "net/http", Method: "ServeFile", CheckArgs: []int{2}}, + {Package: "net/http", Method: "ServeFileFS", CheckArgs: []int{3}}, }, Sanitizers: []taint.Sanitizer{ // filepath.Clean normalizes and removes traversal components {Package: "path/filepath", Method: "Clean"}, + // filepath.Abs calls Clean internally (per Go docs) + {Package: "path/filepath", Method: "Abs"}, // filepath.Base extracts just the filename, removing directory traversal {Package: "path/filepath", Method: "Base"}, // filepath.Rel computes a relative path safely diff --git a/vendor/github.com/securego/gosec/v2/analyzers/range_analyzer.go b/vendor/github.com/securego/gosec/v2/analyzers/range_analyzer.go index 42a4ad80..962f189b 100644 --- a/vendor/github.com/securego/gosec/v2/analyzers/range_analyzer.go +++ b/vendor/github.com/securego/gosec/v2/analyzers/range_analyzer.go @@ -494,6 +494,12 @@ func (ra *RangeAnalyzer) isNonNegativeRecursive(v ssa.Value) bool { return true } + // Elements loaded from a []rune slice created by converting a + // string are valid Unicode code points, guaranteed non-negative. + if isElementOfStringRuneSlice(v) { + return true + } + v, info := getRealValueFromOperation(v) if info.op == "neg" || info.op == "-" { return false @@ -565,6 +571,44 @@ func (ra *RangeAnalyzer) isNonNegativeRecursive(v ssa.Value) bool { return false } +// isElementOfStringRuneSlice checks whether v is a value loaded +// from a []rune slice that was created by converting a string. +// Go guarantees that string-to-[]rune conversions produce valid +// Unicode code points (>= 0), so every element is non-negative. +func isElementOfStringRuneSlice(v ssa.Value) bool { + unOp, ok := v.(*ssa.UnOp) + if !ok || unOp.Op != token.MUL { + return false + } + idx, ok := unOp.X.(*ssa.IndexAddr) + if !ok { + return false + } + return isStringToRuneConversion(idx.X) +} + +// isStringToRuneConversion returns true when v is a Convert from +// string to []int32 (i.e. []rune). +func isStringToRuneConversion(v ssa.Value) bool { + conv, ok := v.(*ssa.Convert) + if !ok { + return false + } + srcBasic, ok := conv.X.Type().Underlying().(*types.Basic) + if !ok || srcBasic.Kind() != types.String { + return false + } + dstSlice, ok := conv.Type().Underlying().(*types.Slice) + if !ok { + return false + } + elemBasic, ok := dstSlice.Elem().Underlying().(*types.Basic) + if !ok { + return false + } + return elemBasic.Kind() == types.Int32 +} + func (ra *RangeAnalyzer) ComputeRange(v ssa.Value, block *ssa.BasicBlock) *rangeResult { res := ra.acquireResult() isSrcUnsigned := isUint(v) @@ -965,7 +1009,7 @@ func (ra *RangeAnalyzer) ComputeRange(v ssa.Value, block *ssa.BasicBlock) *range res.minValue = minBounds(res.minValue, res.minValueSet, argRes.minValue, argRes.minValueSet, isSrcUnsigned) res.minValueSet = res.minValueSet && argRes.minValueSet res.maxValue = minBounds(res.maxValue, res.maxValueSet, argRes.maxValue, argRes.maxValueSet, isSrcUnsigned) - res.maxValueSet = res.maxValueSet && argRes.maxValueSet + res.maxValueSet = res.maxValueSet || argRes.maxValueSet } ra.releaseResult(argRes) } @@ -979,7 +1023,7 @@ func (ra *RangeAnalyzer) ComputeRange(v ssa.Value, block *ssa.BasicBlock) *range res.CopyFrom(argRes) } else { res.minValue = maxBounds(res.minValue, res.minValueSet, argRes.minValue, argRes.minValueSet, isSrcUnsigned) - res.minValueSet = res.minValueSet && argRes.minValueSet + res.minValueSet = res.minValueSet || argRes.minValueSet res.maxValue = maxBounds(res.maxValue, res.maxValueSet, argRes.maxValue, argRes.maxValueSet, isSrcUnsigned) res.maxValueSet = res.maxValueSet && argRes.maxValueSet } diff --git a/vendor/github.com/securego/gosec/v2/analyzers/ssti.go b/vendor/github.com/securego/gosec/v2/analyzers/ssti.go new file mode 100644 index 00000000..f40d74d5 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/analyzers/ssti.go @@ -0,0 +1,104 @@ +// (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" + + "github.com/securego/gosec/v2/taint" +) + +// SSTI returns a configuration for detecting Server-Side Template Injection +// vulnerabilities via text/template. +// +// The text/template package performs NO auto-escaping and allows calling any +// exported method on the data object passed to Execute. When user-controlled +// input flows into Template.Parse, an attacker can invoke arbitrary methods, +// read files, or achieve remote code execution depending on available gadgets. +// +// Even when the template string is static, rendering user data through +// text/template into an HTTP response produces unescaped HTML, enabling XSS. +func SSTI() taint.Config { + return taint.Config{ + Sources: []taint.Source{ + // Type sources: tainted when received as parameters + {Package: "net/http", Name: "Request", Pointer: true}, + {Package: "net/url", Name: "Values"}, + + // Function sources + {Package: "os", Name: "Args", IsFunc: true}, + {Package: "os", Name: "Getenv", IsFunc: true}, + + // I/O sources + {Package: "bufio", Name: "Reader", Pointer: true}, + {Package: "bufio", Name: "Scanner", Pointer: true}, + }, + Sinks: []taint.Sink{ + // CRITICAL: user input flows into the template string itself. + // Template.Parse takes a single string argument (the template text). + {Package: "text/template", Receiver: "Template", Method: "Parse", Pointer: true, CheckArgs: []int{1}}, + + // text/template.Must wraps Parse; arg[0] is the (*Template, error) pair + // but in practice the taint flows through the Parse call above. + + // HIGH: text/template.Execute writes unescaped output to an HTTP response. + // Guard: only flag when the writer (arg 1) implements net/http.ResponseWriter. + { + Package: "text/template", + Receiver: "Template", + Method: "Execute", + Pointer: true, + CheckArgs: []int{2}, + ArgTypeGuards: map[int]string{1: "net/http.ResponseWriter"}, + }, + { + Package: "text/template", + Receiver: "Template", + Method: "ExecuteTemplate", + Pointer: true, + CheckArgs: []int{3}, + ArgTypeGuards: map[int]string{1: "net/http.ResponseWriter"}, + }, + }, + Sanitizers: []taint.Sanitizer{ + // HTML escaping neutralizes both SSTI template directives and XSS payloads + {Package: "html", Method: "EscapeString"}, + {Package: "html/template", Method: "HTMLEscapeString"}, + {Package: "html/template", Method: "JSEscapeString"}, + {Package: "net/url", Method: "QueryEscape"}, + {Package: "net/url", Method: "PathEscape"}, + + // Numeric conversions produce safe output + {Package: "strconv", Method: "Atoi"}, + {Package: "strconv", Method: "Itoa"}, + {Package: "strconv", Method: "ParseInt"}, + {Package: "strconv", Method: "ParseUint"}, + {Package: "strconv", Method: "ParseFloat"}, + {Package: "strconv", Method: "FormatInt"}, + {Package: "strconv", Method: "FormatUint"}, + {Package: "strconv", Method: "FormatFloat"}, + }, + } +} + +// newSSTIAnalyzer creates an analyzer for detecting Server-Side Template +// Injection vulnerabilities via taint analysis (G708). +func newSSTIAnalyzer(id string, description string) *analysis.Analyzer { + config := SSTI() + rule := SSTIRule + rule.ID = id + rule.Description = description + return taint.NewGosecAnalyzer(&rule, &config) +} diff --git a/vendor/github.com/securego/gosec/v2/analyzers/unsafe_deserialization.go b/vendor/github.com/securego/gosec/v2/analyzers/unsafe_deserialization.go new file mode 100644 index 00000000..bbdac51e --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/analyzers/unsafe_deserialization.go @@ -0,0 +1,87 @@ +// (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" + + "github.com/securego/gosec/v2/taint" +) + +// UnsafeDeserialization returns a configuration for detecting unsafe +// deserialization of untrusted data. +// +// Go's encoding/gob package embeds full type information in its wire format +// and will instantiate arbitrary registered types during decode. When an HTTP +// handler passes r.Body directly to gob.NewDecoder().Decode(), an attacker +// controls which types get instantiated, leading to denial-of-service or +// potential RCE (CVE-2024-34156). +// +// gopkg.in/yaml.v2's Unmarshal into interface{} can instantiate arbitrary Go +// types via YAML tags. encoding/xml is susceptible to deeply-nested structure +// DoS and external entity expansion. +func UnsafeDeserialization() taint.Config { + return taint.Config{ + Sources: []taint.Source{ + // Type sources: tainted when received as parameters + {Package: "net/http", Name: "Request", Pointer: true}, + {Package: "net/url", Name: "Values"}, + + // Function sources + {Package: "os", Name: "Args", IsFunc: true}, + {Package: "os", Name: "Getenv", IsFunc: true}, + + // I/O sources + {Package: "bufio", Name: "Reader", Pointer: true}, + {Package: "bufio", Name: "Scanner", Pointer: true}, + }, + Sinks: []taint.Sink{ + // encoding/gob — highest risk: arbitrary type instantiation from wire format + // gob.NewDecoder takes an io.Reader (arg 0), so if the reader is tainted + // the decoder will process attacker-controlled data. + {Package: "encoding/gob", Method: "NewDecoder", CheckArgs: []int{0}}, + + // gopkg.in/yaml.v2 — Unmarshal([]byte, interface{}) can instantiate arbitrary types + {Package: "gopkg.in/yaml.v2", Method: "Unmarshal", CheckArgs: []int{0}}, + // yaml.NewDecoder takes an io.Reader (arg 0) + {Package: "gopkg.in/yaml.v2", Method: "NewDecoder", CheckArgs: []int{0}}, + + // encoding/xml — deeply-nested structure DoS, entity expansion + {Package: "encoding/xml", Method: "NewDecoder", CheckArgs: []int{0}}, + {Package: "encoding/xml", Method: "Unmarshal", CheckArgs: []int{0}}, + }, + Sanitizers: []taint.Sanitizer{ + // io.LimitReader bounds the amount of data read, mitigating DoS amplification + {Package: "io", Method: "LimitReader"}, + + // Numeric conversions — result is safe + {Package: "strconv", Method: "Atoi"}, + {Package: "strconv", Method: "Itoa"}, + {Package: "strconv", Method: "ParseInt"}, + {Package: "strconv", Method: "ParseUint"}, + {Package: "strconv", Method: "ParseFloat"}, + }, + } +} + +// newUnsafeDeserializationAnalyzer creates an analyzer for detecting unsafe +// deserialization of untrusted data via taint analysis (G709). +func newUnsafeDeserializationAnalyzer(id string, description string) *analysis.Analyzer { + config := UnsafeDeserialization() + rule := UnsafeDeserializationRule + rule.ID = id + rule.Description = description + return taint.NewGosecAnalyzer(&rule, &config) +} diff --git a/vendor/github.com/securego/gosec/v2/config.go b/vendor/github.com/securego/gosec/v2/config.go index 69b8f13e..954daaba 100644 --- a/vendor/github.com/securego/gosec/v2/config.go +++ b/vendor/github.com/securego/gosec/v2/config.go @@ -33,6 +33,15 @@ const ( IncludeRules GlobalOption = "include" // SSA global option to enable go analysis framework with SSA support SSA GlobalOption = "ssa" + // NoSecRequireRules global option requires at least one rule ID in every + // #nosec / //gosec:disable annotation. When enabled, naked directives no + // longer suppress any findings and an error is reported instead. + NoSecRequireRules GlobalOption = "nosec-require-rules" + // NoSecRequireJustification global option requires a `-- justification` + // in every #nosec / //gosec:disable annotation. When enabled, directives + // without a justification no longer suppress any findings and an error is + // reported instead. + NoSecRequireJustification GlobalOption = "nosec-require-justification" ) // NoSecTag returns the tag used to disable gosec for a line of code. diff --git a/vendor/github.com/securego/gosec/v2/cwe/data.go b/vendor/github.com/securego/gosec/v2/cwe/data.go index c8bfc27d..16f7645c 100644 --- a/vendor/github.com/securego/gosec/v2/cwe/data.go +++ b/vendor/github.com/securego/gosec/v2/cwe/data.go @@ -48,6 +48,11 @@ var idWeaknesses = map[string]*Weakness{ Description: "The software does not properly neutralize CRLF sequences before using externally-influenced input in protocol elements that rely on CRLF as delimiters, allowing attackers to inject additional commands or headers.", Name: "Improper Neutralization of CRLF Sequences ('CRLF Injection')", }, + "94": { + ID: "94", + Description: "The software constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.", + Name: "Improper Control of Generation of Code ('Code Injection')", + }, "118": { ID: "118", Description: "The software does not restrict or incorrectly restricts operations within the boundaries of a resource that is accessed using an index or pointer, such as memory or files.", @@ -163,6 +168,26 @@ var idWeaknesses = map[string]*Weakness{ Description: "The product uses a cryptographic primitive that uses an Initialization Vector (IV), but the product does not generate IVs that are sufficiently unpredictable or unique according to the expected cryptographic requirements for that primitive.", Name: "Generation of Weak Initialization Vector (IV)", }, + "117": { + ID: "117", + Description: "The software does not neutralize or incorrectly neutralizes output that is written to logs.", + Name: "Improper Output Neutralization for Logs", + }, + "502": { + ID: "502", + Description: "The application deserializes untrusted data without sufficiently verifying that the resulting data will be valid.", + Name: "Deserialization of Untrusted Data", + }, + "614": { + ID: "614", + Description: "The Secure attribute for a sensitive cookie is not set, which could cause the user agent to send that cookie in plaintext over an HTTP session.", + Name: "Sensitive Cookie in HTTPS Session Without 'Secure' Attribute", + }, + "918": { + ID: "918", + Description: "The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.", + Name: "Server-Side Request Forgery (SSRF)", + }, } // Get Retrieves a CWE weakness by it's id diff --git a/vendor/github.com/securego/gosec/v2/issue/issue.go b/vendor/github.com/securego/gosec/v2/issue/issue.go index c0d485f7..f2cc986d 100644 --- a/vendor/github.com/securego/gosec/v2/issue/issue.go +++ b/vendor/github.com/securego/gosec/v2/issue/issue.go @@ -52,7 +52,9 @@ func GetCweByRule(id string) *cwe.Weakness { return nil } -// ruleToCWE maps gosec rules to CWEs +// ruleToCWE maps gosec rules to CWEs. The key is the rule ID +// and the value is the CWE ID. If a rule does not have a CWE, +// it will not be included in this map. var ruleToCWE = map[string]string{ "G101": "798", "G102": "200", @@ -65,7 +67,10 @@ var ruleToCWE = map[string]string{ "G110": "409", "G111": "22", "G112": "400", + "G113": "444", "G707": "93", + "G708": "94", + "G709": "502", "G114": "676", "G115": "190", "G116": "838", @@ -76,6 +81,7 @@ var ruleToCWE = map[string]string{ "G121": "346", "G122": "367", "G123": "295", + "G124": "614", "G201": "89", "G202": "89", "G203": "79", @@ -86,6 +92,7 @@ var ruleToCWE = map[string]string{ "G304": "22", "G305": "22", "G306": "276", + "G307": "276", "G401": "328", "G402": "295", "G403": "310", @@ -103,6 +110,13 @@ var ruleToCWE = map[string]string{ "G507": "327", "G601": "118", "G602": "118", + "G701": "89", + "G702": "78", + "G703": "22", + "G704": "918", + "G705": "79", + "G706": "117", + "G710": "601", } // Issue is returned by a gosec rule if it discovers an issue with the scanned code. diff --git a/vendor/github.com/securego/gosec/v2/renovate.json b/vendor/github.com/securego/gosec/v2/renovate.json index 58ee1e0e..9f03abc0 100644 --- a/vendor/github.com/securego/gosec/v2/renovate.json +++ b/vendor/github.com/securego/gosec/v2/renovate.json @@ -1,25 +1,52 @@ { - "dependencyDashboard": true, - "dependencyDashboardTitle" : "Renovate(bot) : dependency dashboard", - "vulnerabilityAlerts": { - "enabled": true - }, - "extends": [ - ":preserveSemverRanges", - "group:all", - "schedule:weekly" - ], - "lockFileMaintenance": { - "commitMessageAction": "Update", - "enabled": true, + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "dependencyDashboard": true, + "dependencyDashboardTitle": "Renovate(bot) : dependency dashboard", + "vulnerabilityAlerts": { + "enabled": true + }, "extends": [ - "group:all", - "schedule:weekly" - ] - }, - "postUpdateOptions": [ - "gomodTidy", - "gomodUpdateImportPaths" - ], - "separateMajorMinor": false + "config:best-practices", + "helpers:pinGitHubActionDigests", + "schedule:weekly" + ], + "recreateWhen": "always", + "packageRules": [ + { + "matchPackageNames": ["*"], + "groupName": "all dependencies", + "groupSlug": "all" + }, + { + "description": "Run pin updates immediately, outside of weekly schedule", + "matchUpdateTypes": ["pin"], + "schedule": ["at any time"] + }, + { + "description": "Skip digest pinning for bare branch refs - these cannot be pinned reliably", + "matchPackageNames": ["ccojocar/barry", "securego/gosec"], + "matchManagers": ["github-actions"], + "pinDigests": false + }, + { + "description": "Enable vulnerability alerts for indirect Go dependencies", + "matchDepTypes": ["indirect"], + "matchManagers": ["gomod"], + "enabled": true, + "vulnerabilityAlerts": { + "enabled": true + } + } + ], + "lockFileMaintenance": { + "commitMessageAction": "Update", + "enabled": true, + "schedule": ["before 3am on monday"], + "branchPrefix": "renovate/" + }, + "postUpdateOptions": [ + "gomodTidy", + "gomodUpdateImportPaths" + ], + "separateMajorMinor": false } 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 b0d5fdad..c0639a3d 100644 --- a/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go +++ b/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go @@ -63,6 +63,10 @@ var secretsPatterns = [...]secretPattern{ name: "AWS API Key", regexp: regexp.MustCompile(`AKIA[0-9A-Z]{16}`), }, + { + name: "AWS Temporary Access Key", + regexp: regexp.MustCompile(`ASIA[0-9A-Z]{16}`), + }, { name: "Amazon MWS Auth Token", regexp: regexp.MustCompile(`amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`), @@ -163,6 +167,19 @@ var secretsPatterns = [...]secretPattern{ name: "Twitter OAuth", regexp: regexp.MustCompile(`[tT][wW][iI][tT][tT][eE][rR].*[''|"][0-9a-zA-Z]{35,44}[''|"]`), }, + + { + name: "GitHub personal access token", + regexp: regexp.MustCompile(`ghp_[a-zA-Z0-9]{36}`), + }, + { + name: "GitHub fine-grained access token", + regexp: regexp.MustCompile(`github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}`), + }, + { + name: "GitHub action temporary token", + regexp: regexp.MustCompile(`ghs_[a-zA-Z0-9]{36}`), + }, } type credentials struct { diff --git a/vendor/github.com/securego/gosec/v2/rules/rand.go b/vendor/github.com/securego/gosec/v2/rules/rand.go index a1d3508a..e41969f7 100644 --- a/vendor/github.com/securego/gosec/v2/rules/rand.go +++ b/vendor/github.com/securego/gosec/v2/rules/rand.go @@ -30,10 +30,10 @@ func NewWeakRandCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { rule := &weakRand{newCallListRule(id, "Use of weak random number generator (math/rand or math/rand/v2 instead of crypto/rand)", issue.High, issue.Medium)} - rule.AddAll("math/rand", "New", "Read", "Float32", "Float64", "Int", "Int31", "Int31n", - "Int63", "Int63n", "Intn", "NormFloat64", "Uint32", "Uint64") - rule.AddAll("math/rand/v2", "New", "Float32", "Float64", "Int", "Int32", "Int32N", - "Int64", "Int64N", "IntN", "N", "NormFloat64", "Uint32", "Uint32N", "Uint64", "Uint64N", "UintN") + rule.AddAll("math/rand", "New", "Read", "ExpFloat64", "Float32", "Float64", "Int", "Int31", "Int31n", + "Int63", "Int63n", "Intn", "NormFloat64", "Perm", "Shuffle", "Uint32", "Uint64") + rule.AddAll("math/rand/v2", "New", "ExpFloat64", "Float32", "Float64", "Int", "Int32", "Int32N", + "Int64", "Int64N", "IntN", "N", "NormFloat64", "Perm", "Shuffle", "Uint", "Uint32", "Uint32N", "Uint64", "Uint64N", "UintN") return rule, []ast.Node{(*ast.CallExpr)(nil)} } diff --git a/vendor/github.com/securego/gosec/v2/rules/secret_serialization.go b/vendor/github.com/securego/gosec/v2/rules/secret_serialization.go index 06473faa..6389309c 100644 --- a/vendor/github.com/securego/gosec/v2/rules/secret_serialization.go +++ b/vendor/github.com/securego/gosec/v2/rules/secret_serialization.go @@ -21,10 +21,11 @@ type secretSerialization struct { } type formatSpec struct { - name string - tagKey string - functionSinks []functionSink - methodSinks []methodSink + name string + tagKey string + marshalerMethod string // e.g. "MarshalJSON"; empty if no standard interface exists + functionSinks []functionSink + methodSinks []methodSink } type functionSink struct { @@ -44,15 +45,16 @@ type typeAnalysisCacheKey struct { } type sensitiveFieldMatch struct { - fieldName string - jsonKey string - found bool + fieldName string + serializedKey string + found bool } var g117Formats = []formatSpec{ { - name: "json", - tagKey: "json", + name: "JSON", + tagKey: "json", + marshalerMethod: "MarshalJSON", functionSinks: []functionSink{ {pkgPath: "encoding/json", names: []string{"Marshal", "MarshalIndent"}}, }, @@ -61,8 +63,9 @@ var g117Formats = []formatSpec{ }, }, { - name: "yaml", - tagKey: "yaml", + name: "YAML", + tagKey: "yaml", + marshalerMethod: "MarshalYAML", functionSinks: []functionSink{ {pkgPath: "go.yaml.in/yaml/v3", names: []string{"Marshal"}}, {pkgPath: "gopkg.in/yaml.v3", names: []string{"Marshal"}}, @@ -76,8 +79,9 @@ var g117Formats = []formatSpec{ }, }, { - name: "xml", - tagKey: "xml", + name: "XML", + tagKey: "xml", + marshalerMethod: "MarshalXML", functionSinks: []functionSink{ {pkgPath: "encoding/xml", names: []string{"Marshal", "MarshalIndent"}}, }, @@ -86,7 +90,7 @@ var g117Formats = []formatSpec{ }, }, { - name: "toml", + name: "TOML", tagKey: "toml", functionSinks: []functionSink{ {pkgPath: "github.com/pelletier/go-toml", names: []string{"Marshal"}}, @@ -111,17 +115,146 @@ func (r *secretSerialization) Match(n ast.Node, ctx *gosec.Context) (*issue.Issu return nil, nil } + if isInsideCustomMarshaler(callExpr, ctx) { + return nil, nil + } + typ := ctx.Info.TypeOf(serializedArg) if typ == nil { return nil, nil } - if match := r.findSensitiveFieldForType(typ, format.tagKey); match.found { - msg := fmt.Sprintf("Marshaled struct field %q (JSON key %q) matches secret pattern", match.fieldName, match.jsonKey) - return ctx.NewIssue(callExpr, r.ID(), msg, r.Severity, r.Confidence), nil + if typeImplementsMarshaler(typ, format.marshalerMethod) { + return nil, nil + } + + match := r.findSensitiveFieldForType(typ, format.tagKey) + if !match.found { + return nil, nil + } + + if compositeLitFieldIsTransformed(serializedArg, match.fieldName) { + return nil, nil + } + + msg := fmt.Sprintf("Marshaled struct field %q (%s key %q) matches secret pattern", match.fieldName, format.name, match.serializedKey) + return ctx.NewIssue(callExpr, r.ID(), msg, r.Severity, r.Confidence), nil +} + +// customMarshalerMethods lists method names that indicate a custom marshaler +// implementation. When a marshal call occurs inside one of these methods, the +// developer is explicitly controlling serialization, so G117 should not flag it. +var customMarshalerMethods = map[string]bool{ + "MarshalJSON": true, + "MarshalYAML": true, + "MarshalXML": true, + "MarshalText": true, + "MarshalTOML": true, + "MarshalBSON": true, +} + +// isInsideCustomMarshaler reports whether callExpr is located inside a method +// whose name matches a known custom marshaler (e.g. MarshalJSON). +func isInsideCustomMarshaler(callExpr *ast.CallExpr, ctx *gosec.Context) bool { + if ctx.Root == nil { + return false + } + + pos := callExpr.Pos() + var found bool + + ast.Inspect(ctx.Root, func(n ast.Node) bool { + if found { + return false + } + funcDecl, ok := n.(*ast.FuncDecl) + if !ok || funcDecl.Body == nil { + return true + } + // Check if the call is inside this function body. + if pos < funcDecl.Body.Pos() || pos >= funcDecl.Body.End() { + return true + } + // Must be a method (has a receiver) with a recognized marshaler name. + if funcDecl.Recv != nil && funcDecl.Recv.NumFields() > 0 { + if customMarshalerMethods[funcDecl.Name.Name] { + found = true + } + } + return false + }) + + return found +} + +// typeImplementsMarshaler reports whether typ (or its element type for +// containers) has a method with the given name, indicating it implements a +// custom marshaler interface (e.g. json.Marshaler). When a type has a custom +// marshaler, the serialization library calls that method instead of serializing +// fields directly, making struct field analysis irrelevant. +func typeImplementsMarshaler(typ types.Type, methodName string) bool { + if methodName == "" { + return false + } + named := elementNamedType(typ) + if named == nil { + return false + } + // Check both value and pointer receiver methods via the pointer method set, + // which is a superset of the value method set. + mset := types.NewMethodSet(types.NewPointer(named)) + for i := 0; i < mset.Len(); i++ { + if mset.At(i).Obj().Name() == methodName { + return true + } + } + return false +} + +// elementNamedType unwraps pointers, slices, arrays, and maps to find the +// innermost Named type. Returns nil if no Named type is found. +func elementNamedType(typ types.Type) *types.Named { + switch t := typ.(type) { + case *types.Named: + return t + case *types.Pointer: + return elementNamedType(t.Elem()) + case *types.Slice: + return elementNamedType(t.Elem()) + case *types.Array: + return elementNamedType(t.Elem()) + case *types.Map: + return elementNamedType(t.Elem()) } + return nil +} - return nil, nil +// compositeLitFieldIsTransformed checks whether expr is a composite literal +// in which the given field name is assigned a function call result. A function +// call indicates the value is being transformed (e.g. masked or redacted) +// before serialization. +func compositeLitFieldIsTransformed(expr ast.Expr, fieldName string) bool { + // Unwrap address-of operator: &Struct{...} + if unary, ok := expr.(*ast.UnaryExpr); ok { + expr = unary.X + } + lit, ok := expr.(*ast.CompositeLit) + if !ok { + return false + } + for _, elt := range lit.Elts { + kv, ok := elt.(*ast.KeyValueExpr) + if !ok { + continue + } + ident, ok := kv.Key.(*ast.Ident) + if !ok || ident.Name != fieldName { + continue + } + _, isCall := kv.Value.(*ast.CallExpr) + return isCall + } + return false } func isNamedTypeInPackage(typ types.Type, pkgPath, typeName string) bool { @@ -382,7 +515,7 @@ func (r *secretSerialization) findSensitiveSerializedField(st *types.Struct, tag } if gosec.RegexMatchWithCache(r.pattern, field.Name()) || gosec.RegexMatchWithCache(r.pattern, effectiveKey) { - return sensitiveFieldMatch{fieldName: field.Name(), jsonKey: effectiveKey, found: true} + return sensitiveFieldMatch{fieldName: field.Name(), serializedKey: effectiveKey, found: true} } } diff --git a/vendor/github.com/securego/gosec/v2/rules/tls.go b/vendor/github.com/securego/gosec/v2/rules/tls.go index 96b243df..6f7cb399 100644 --- a/vendor/github.com/securego/gosec/v2/rules/tls.go +++ b/vendor/github.com/securego/gosec/v2/rules/tls.go @@ -236,22 +236,28 @@ func (t *insecureConfigTLS) findDefinition(obj types.Object, c *gosec.Context) a return initializer } +func (t *insecureConfigTLS) isSafeDefault() bool { + major, minor, _ := gosec.GoVersion() + return major > 1 || (major == 1 && minor >= 18) +} + func (t *insecureConfigTLS) checkVersion(n ast.Node, c *gosec.Context) *issue.Issue { - // Flag explicitly low MinVersion (including explicit 0) + // Flag explicitly low MinVersion. + // Since Go 1.18+, MinVersion 0 means "use default" which is + // TLS 1.2 — safe and not worth flagging. if t.minVersionSet && t.actualMinVersion < t.MinVersion { + if t.actualMinVersion == 0 && t.isSafeDefault() { + return nil + } return c.NewIssue(n, t.ID(), "TLS MinVersion too low.", issue.High, issue.High) } - // Handle MaxVersion + // Handle MaxVersion. + // MaxVersion 0 means "use latest" which is always safe. if t.maxVersionSet { - // Special case for explicit MaxVersion: 0 (default latest) - suppress warning if MinVersion is securely set if t.actualMaxVersion == 0 { - if t.minVersionSet && t.actualMinVersion >= t.MinVersion { - return nil - } - // Otherwise treat explicit 0 as potentially insecure (fall through to flag) + return nil } - // Flag if explicitly capped too low (non-zero low values always flagged) if t.actualMaxVersion < t.MaxVersion { return c.NewIssue(n, t.ID(), "TLS MaxVersion too low.", issue.High, issue.High) } @@ -271,6 +277,7 @@ func (t *insecureConfigTLS) Match(n ast.Node, c *gosec.Context) (*issue.Issue, e if complit, ok := n.(*ast.CompositeLit); ok && complit.Type != nil { actualType := c.Info.TypeOf(complit.Type) if actualType != nil && actualType.String() == t.requiredType { + defer t.resetVersion() for _, elt := range complit.Elts { if issue := t.processTLSConf(elt, c); issue != nil { return issue, nil @@ -279,7 +286,6 @@ func (t *insecureConfigTLS) Match(n ast.Node, c *gosec.Context) (*issue.Issue, e if issue := t.checkVersion(complit, c); issue != nil { return issue, nil } - t.resetVersion() return nil, nil } } diff --git a/vendor/github.com/securego/gosec/v2/taint/taint.go b/vendor/github.com/securego/gosec/v2/taint/taint.go index d53b14f2..fab64ccc 100644 --- a/vendor/github.com/securego/gosec/v2/taint/taint.go +++ b/vendor/github.com/securego/gosec/v2/taint/taint.go @@ -23,6 +23,12 @@ import ( // maxTaintDepth limits recursion depth to prevent stack overflow on large codebases const maxTaintDepth = 50 +// maxCallerEdges caps the number of incoming call graph edges examined per function +// in isParameterTainted. CHA over-approximates call graphs (every interface method +// call fans out to ALL implementations), so a function can have thousands of callers. +// Real taint flows come from direct/nearby callers, not the 33rd+ CHA-generated edge. +const maxCallerEdges = 32 + // isContextType checks if a type is context.Context. // context.Context is a control-flow mechanism (deadlines, cancellation, request-scoped values) // that does not carry user-controlled data relevant to taint sinks like XSS. @@ -211,14 +217,21 @@ type Config struct { } // Analyzer performs taint analysis on SSA programs. +// paramKey identifies a specific parameter of a function for memoization. +type paramKey struct { + fn *ssa.Function + paramIdx int +} + type Analyzer struct { - config *Config - sources map[string]Source // keyed by full type string - funcSrcs map[string]Source // function sources keyed by "pkg.Func" - sinks map[string]Sink // keyed by full function string - sanitizers map[string]struct{} // keyed by full function string - callGraph *callgraph.Graph - prog *ssa.Program // set at Analyze time for ArgTypeGuards resolution + config *Config + sources map[string]Source // keyed by full type string + funcSrcs map[string]Source // function sources keyed by "pkg.Func" + sinks map[string]Sink // keyed by full function string + sanitizers map[string]struct{} // keyed by full function string + callGraph *callgraph.Graph + prog *ssa.Program // set at Analyze time for ArgTypeGuards resolution + paramTaintCache map[paramKey]bool // caches true results from isParameterTainted } // SetCallGraph injects a precomputed call graph. @@ -309,6 +322,8 @@ func (a *Analyzer) Analyze(prog *ssa.Program, srcFuncs []*ssa.Function) []Result a.callGraph = cha.CallGraph(prog) } + a.paramTaintCache = make(map[paramKey]bool) + var results []Result // Find all sink calls in the program @@ -316,6 +331,8 @@ func (a *Analyzer) Analyze(prog *ssa.Program, srcFuncs []*ssa.Function) []Result results = append(results, a.analyzeFunctionSinks(fn)...) } + a.paramTaintCache = nil + return results } @@ -794,6 +811,33 @@ func (a *Analyzer) isSourceType(t types.Type) bool { return false } +// mayHaveExternalCallers reports whether fn could be invoked by code outside +// the analyzed package — code that is invisible to the call graph. +// +// Exported bare functions (non-methods) are the primary case: frameworks +// register them via dynamic dispatch that CHA cannot resolve, so the call +// graph may lack edges even though the function IS called at runtime. +// +// Methods with a receiver are excluded because CHA resolves interface dispatch +// to concrete methods, so their callers are generally visible in the graph. +// Unexported functions are only callable within the package, and the call graph +// covers intra-package calls comprehensively. +func mayHaveExternalCallers(fn *ssa.Function) bool { + if fn.Signature == nil { + return false + } + // Methods — CHA handles interface dispatch; callers are visible. + if fn.Signature.Recv() != nil { + return false + } + // Closures / anonymous functions are never exported. + if fn.Parent() != nil { + return false + } + // Exported bare function — may be called by external frameworks. + return token.IsExported(fn.Name()) +} + // isSourceFuncCall checks if a call invokes a known source function // (a function explicitly configured as producing tainted data, e.g., os.Getenv). func (a *Analyzer) isSourceFuncCall(call *ssa.Call) bool { @@ -824,30 +868,65 @@ func (a *Analyzer) isParameterTainted(param *ssa.Parameter, fn *ssa.Function, vi return false } - // Check if parameter type is a source type. - // This is the ONLY place where type-based source matching should trigger - // automatic taint — because parameters represent data flowing IN from - // external callers we don't control. - if a.isSourceType(param.Type()) { - return true + // Resolve paramIdx early so we can use it for cache lookups. + paramIdx := -1 + for i, p := range fn.Params { + if p == param { + paramIdx = i + break + } + } + + // Check memoization cache (only true results are cached). + if paramIdx >= 0 && a.paramTaintCache != nil { + key := paramKey{fn: fn, paramIdx: paramIdx} + if a.paramTaintCache[key] { + return true + } } // Use call graph to find callers and check their arguments if a.callGraph == nil { + // No call graph: fall back to type-based auto-taint for source-typed params + // (conservative — may produce false positives, but we have no callee info). + if a.isSourceType(param.Type()) { + if paramIdx >= 0 && a.paramTaintCache != nil { + a.paramTaintCache[paramKey{fn: fn, paramIdx: paramIdx}] = true + } + return true + } return false } node := a.callGraph.Nodes[fn] - if node == nil { - return false - } - paramIdx := -1 - for i, p := range fn.Params { - if p == param { - paramIdx = i - break + // Check if parameter type is a configured source type. + // + // Strategy: + // 1. No callers in call graph → definite entry point → auto-taint. + // 2. Exported bare function → may have invisible external callers + // (framework dispatch) → auto-taint to avoid false negatives. + // 3. Has callers, not exported bare func → fall through to caller check. + // + // Case 2 addresses a class of false negatives where an internal caller + // with safe args suppresses taint for an exported entry point that is + // also called externally by a framework (issue #1629 + Barry review). + // Methods are excluded because CHA resolves interface dispatch, making + // their callers visible in the call graph. + if a.isSourceType(param.Type()) { + isEntryPoint := (node == nil || len(node.In) == 0) + if isEntryPoint || mayHaveExternalCallers(fn) { + if paramIdx >= 0 && a.paramTaintCache != nil { + a.paramTaintCache[paramKey{fn: fn, paramIdx: paramIdx}] = true + } + return true } + // Has known callers and is not a handler — fall through to verify + // taint via those callers. + } + + if node == nil { + return false } if paramIdx < 0 { @@ -867,8 +946,14 @@ func (a *Analyzer) isParameterTainted(param *ssa.Parameter, fn *ssa.Function, vi adjustedIdx = paramIdx } - // Check each caller + // Check each caller, capping at maxCallerEdges to avoid combinatorial + // explosion from CHA over-approximation of interface method calls. + edgesChecked := 0 for _, inEdge := range node.In { + if edgesChecked >= maxCallerEdges { + break + } + site := inEdge.Site if site == nil { continue @@ -877,7 +962,11 @@ func (a *Analyzer) isParameterTainted(param *ssa.Parameter, fn *ssa.Function, vi callArgs := site.Common().Args if adjustedIdx < len(callArgs) { + edgesChecked++ if a.isTainted(callArgs[adjustedIdx], inEdge.Caller.Func, visited, depth+1) { + if a.paramTaintCache != nil { + a.paramTaintCache[paramKey{fn: fn, paramIdx: paramIdx}] = true + } return true } } @@ -1018,6 +1107,10 @@ func (a *Analyzer) isFieldTaintedOnValue(v ssa.Value, fieldIdx int, fn *ssa.Func case *ssa.Alloc: return a.isFieldOfAllocTainted(val, fieldIdx, fn, visited, depth) case *ssa.Phi: + if visited[v] { + return false + } + visited[v] = true for _, edge := range val.Edges { if a.isFieldTaintedOnValue(edge, fieldIdx, fn, visited, depth+1) { return true diff --git a/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_darwin.go b/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_darwin.go index d3b6dbc5..847a9162 100644 --- a/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_darwin.go +++ b/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_darwin.go @@ -143,7 +143,7 @@ func perCPUTimes(sys *common.SystemLib) ([]TimesStat, error) { var cpuload *hostCpuLoadInfoData status := sys.HostProcessorInfo(sys.MachHostSelf(), processorCpuLoadInfo, - &ncpu, uintptr(unsafe.Pointer(&cpuload)), &count) + &ncpu, unsafe.Pointer(&cpuload), &count) if status != common.KERN_SUCCESS { return nil, fmt.Errorf("host_processor_info error=%d", status) @@ -177,7 +177,7 @@ func allCPUTimes(sys *common.SystemLib) ([]TimesStat, error) { count := uint32(cpuStateMax) status := sys.HostStatistics(sys.MachHostSelf(), common.HOST_CPU_LOAD_INFO, - uintptr(unsafe.Pointer(&cpuload)), &count) + unsafe.Pointer(&cpuload), &count) if status != common.KERN_SUCCESS { return nil, fmt.Errorf("host_statistics error=%d", status) diff --git a/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_freebsd.go b/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_freebsd.go index 3e0aeb26..38abcd0e 100644 --- a/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_freebsd.go +++ b/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_freebsd.go @@ -81,6 +81,9 @@ func TimesWithContext(_ context.Context, percpu bool) ([]TimesStat, error) { if err != nil { return nil, err } + if len(buf) < int(unsafe.Sizeof(cpuTimes{})) { + return nil, fmt.Errorf("unexpected size: kern.cp_time, %d", len(buf)) + } times := (*cpuTimes)(unsafe.Pointer(&buf[0])) return []TimesStat{*timeStat("cpu-total", times)}, nil diff --git a/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_netbsd.go b/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_netbsd.go index 9e23edb6..bb383129 100644 --- a/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_netbsd.go +++ b/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_netbsd.go @@ -44,6 +44,9 @@ func TimesWithContext(_ context.Context, percpu bool) ([]TimesStat, error) { if err != nil { return ret, err } + if len(buf) < int(unsafe.Sizeof(cpuTimes{})) { + return ret, fmt.Errorf("unexpected size: kern.cp_time, %d", len(buf)) + } times := (*cpuTimes)(unsafe.Pointer(&buf[0])) ret = append(ret, TimesStat{ CPU: "cpu-total", @@ -68,6 +71,9 @@ func TimesWithContext(_ context.Context, percpu bool) ([]TimesStat, error) { if err != nil { return ret, err } + if len(buf) < int(unsafe.Sizeof(cpuTimes{})) { + continue + } stats := (*cpuTimes)(unsafe.Pointer(&buf[0])) ret = append(ret, TimesStat{ diff --git a/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_openbsd.go b/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_openbsd.go index 9b37d296..01654339 100644 --- a/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_openbsd.go +++ b/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_openbsd.go @@ -62,6 +62,9 @@ func TimesWithContext(_ context.Context, percpu bool) ([]TimesStat, error) { if err != nil { return ret, err } + if len(buf) < int(unsafe.Sizeof(cpuTimes{})) { + return ret, fmt.Errorf("unexpected size: kern.cp_time, %d", len(buf)) + } times := (*cpuTimes)(unsafe.Pointer(&buf[0])) ret = append(ret, TimesStat{ CPU: "cpu-total", @@ -86,6 +89,9 @@ func TimesWithContext(_ context.Context, percpu bool) ([]TimesStat, error) { if err != nil { return ret, err } + if len(buf) < int(unsafe.Sizeof(cpuStats{})) { + continue + } stats := (*cpuStats)(unsafe.Pointer(&buf[0])) if (stats.Flags & cpuOnline) == 0 { diff --git a/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_windows.go b/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_windows.go index c15a085b..5a8a344a 100644 --- a/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_windows.go +++ b/vendor/github.com/shirou/gopsutil/v4/cpu/cpu_windows.go @@ -104,14 +104,78 @@ func Times(percpu bool) ([]TimesStat, error) { } func TimesWithContext(_ context.Context, percpu bool) ([]TimesStat, error) { + // Get the CPU performance counters per processor via Windows API + stats, err := perfInfo() + if err == nil && len(stats) == 0 { + err = errors.New("no processor performance information returned") + } + if err != nil { + if percpu { + return nil, err + } + // perfInfo relies on the undocumented NtQuerySystemInformation, which + // may be unavailable in restricted environments. Fall back to the + // public GetSystemTimes for the total rather than failing outright. + return systemTimes() + } + if percpu { - return perCPUTimes() + ret := make([]TimesStat, 0, len(stats)) + for core, v := range stats { + c := TimesStat{ + CPU: fmt.Sprintf("cpu%d", core), + User: float64(v.UserTime) / ClocksPerSec, + System: float64(v.KernelTime-v.IdleTime) / ClocksPerSec, + Idle: float64(v.IdleTime) / ClocksPerSec, + Irq: float64(v.InterruptTime) / ClocksPerSec, + } + ret = append(ret, c) + } + return ret, nil + } + + // Accumulate the times over all CPUs as GetSystemTimes() will only return + // the counters for the current processor group. This causes issues for + // machines with more than 64 logical processors when the current thread is + // switched to another processor group as then the counters are not + // monotonic anymore. + // + // Do all arithmetic on the integer tick counts and convert to float64 only + // once. Converting each counter first loses precision on large counters and + // can make the returned values non-monotonic. See issue #2110. + var idle, kernel, user, interrupt int64 + for _, v := range stats { + idle += v.IdleTime + kernel += v.KernelTime + user += v.UserTime + interrupt += v.InterruptTime } - var ret []TimesStat - var lpIdleTime common.FILETIME - var lpKernelTime common.FILETIME - var lpUserTime common.FILETIME + return []TimesStat{ + { + CPU: "cpu-total", + User: float64(user) / ClocksPerSec, + System: float64(kernel-idle) / ClocksPerSec, // kernel time includes idle time + Idle: float64(idle) / ClocksPerSec, + Irq: float64(interrupt) / ClocksPerSec, + }, + }, nil +} + +// systemTimes returns the combined CPU times reported by GetSystemTimes. +// +// This is only a fallback for TimesWithContext(ctx, false): GetSystemTimes is a +// documented public API but returns the counters of the calling thread's +// processor group only, so on hosts with more than 64 logical processors the +// values are not monotonic once the thread is migrated to another group. It +// also cannot report Irq, which is left at zero here. +// +// Whether perfInfo works is a property of the environment, so in practice a +// process stays on one path for its whole lifetime. Should perfInfo fail only +// intermittently on a multi-group host, the two paths cover a different number +// of processors, and Percent may report one bogus sample before recovering. +func systemTimes() ([]TimesStat, error) { + var lpIdleTime, lpKernelTime, lpUserTime common.FILETIME // GetSystemTimes returns 0 for error, in which case we check err, // see https://pkg.go.dev/golang.org/x/sys/windows#LazyProc.Call r, _, err := common.ProcGetSystemTimes.Call( @@ -122,20 +186,19 @@ func TimesWithContext(_ context.Context, percpu bool) ([]TimesStat, error) { return nil, err } - LOT := float64(0.0000001) - HIT := (LOT * 4294967296.0) - idle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime))) - user := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime))) - kernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime))) - system := (kernel - idle) - - ret = append(ret, TimesStat{ - CPU: "cpu-total", - Idle: float64(idle), - User: float64(user), - System: float64(system), - }) - return ret, nil + // See the note on integer arithmetic in TimesWithContext and issue #2110. + idle := uint64(lpIdleTime.DwHighDateTime)<<32 | uint64(lpIdleTime.DwLowDateTime) + user := uint64(lpUserTime.DwHighDateTime)<<32 | uint64(lpUserTime.DwLowDateTime) + kernel := uint64(lpKernelTime.DwHighDateTime)<<32 | uint64(lpKernelTime.DwLowDateTime) + + return []TimesStat{ + { + CPU: "cpu-total", + User: float64(user) / ClocksPerSec, + System: float64(kernel-idle) / ClocksPerSec, // kernel time includes idle time + Idle: float64(idle) / ClocksPerSec, + }, + }, nil } func Info() ([]InfoStat, error) { @@ -242,35 +305,22 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) { return ret, nil } -// perCPUTimes returns times stat per cpu, per core and overall for all CPUs -func perCPUTimes() ([]TimesStat, error) { - var ret []TimesStat - stats, err := perfInfo() - if err != nil { - return nil, err - } - for core, v := range stats { - c := TimesStat{ - CPU: fmt.Sprintf("cpu%d", core), - User: float64(v.UserTime) / ClocksPerSec, - System: float64(v.KernelTime-v.IdleTime) / ClocksPerSec, - Idle: float64(v.IdleTime) / ClocksPerSec, - Irq: float64(v.InterruptTime) / ClocksPerSec, - } - ret = append(ret, c) - } - return ret, nil -} - // makes call to Windows API function to retrieve performance information for each core func perfInfo() ([]win32_SystemProcessorPerformanceInformation, error) { // On hosts with more than 64 logical CPUs Windows splits CPUs into Processor Groups // (up to 64 logical CPUs per group). The non-Ex NtQuerySystemInformation only returns // data for the calling thread's group, so whenever the Ex variant is available we // iterate every active group and concatenate the results. See issue #887. - if common.ProcNtQuerySystemInformationEx.Find() == nil { + // + // Every proc is resolved with Find before it is used: LazyProc.Call panics when it + // cannot resolve, and a panic would take the caller's process down instead of + // letting TimesWithContext fall back to GetSystemTimes. + if common.ProcNtQuerySystemInformationEx.Find() == nil && procGetActiveProcessorGroupCount.Find() == nil { return perfInfoAllGroups() } + if err := common.ProcNtQuerySystemInformation.Find(); err != nil { + return nil, err + } return perfInfoSingleGroup() } diff --git a/vendor/github.com/shirou/gopsutil/v4/internal/common/common_darwin.go b/vendor/github.com/shirou/gopsutil/v4/internal/common/common_darwin.go index caa1b8d9..2cf72875 100644 --- a/vendor/github.com/shirou/gopsutil/v4/internal/common/common_darwin.go +++ b/vendor/github.com/shirou/gopsutil/v4/internal/common/common_darwin.go @@ -27,16 +27,37 @@ const ( SystemLibPath = "/usr/lib/libSystem.B.dylib" ) +// Library handles are opened once and shared for the process lifetime. +// Opening and closing them on every call causes SIGBUS/SIGSEGV crashes because +// the Go runtime (GC, timers) can interact with invalidated library handles +// after Dlclose. Sharing also keeps the resolved-symbol cache in fnMap warm, +// so a symbol lookup is not repeated on every call. +// See: https://github.com/shirou/gopsutil/issues/1832 +var ( + libCacheMu sync.Mutex + libCache = make(map[string]*library) +) + func newLibrary(path string) (*library, error) { - lib, err := purego.Dlopen(path, purego.RTLD_LAZY|purego.RTLD_GLOBAL) + libCacheMu.Lock() + defer libCacheMu.Unlock() + + if lib, ok := libCache[path]; ok { + return lib, nil + } + + handle, err := purego.Dlopen(path, purego.RTLD_LAZY|purego.RTLD_GLOBAL) if err != nil { return nil, err } - return &library{ - handle: lib, + lib := &library{ + handle: handle, fnMap: make(map[string]any), - }, nil + } + libCache[path] = lib + + return lib, nil } func (lib *library) Dlsym(symbol string) (uintptr, error) { @@ -69,9 +90,10 @@ func getFunc[T any](lib *library, symbol string) T { return dlfun.fn } -func (lib *library) Close() { - purego.Dlclose(lib.handle) -} +// Close is a no-op, kept so that existing defer-Close call sites keep working. +// Library handles are shared process-wide and are deliberately never unloaded; +// see newLibrary for why. +func (*library) Close() {} type dlFunc[T any] struct { sym string @@ -103,12 +125,12 @@ func (c *CoreFoundationLib) CFGetTypeID(cf uintptr) int64 { return fn(cf) } -func (c *CoreFoundationLib) CFNumberCreate(allocator uintptr, theType int64, valuePtr uintptr) unsafe.Pointer { +func (c *CoreFoundationLib) CFNumberCreate(allocator uintptr, theType int64, valuePtr unsafe.Pointer) unsafe.Pointer { fn := getFunc[CFNumberCreateFunc](c.library, "CFNumberCreate") return fn(allocator, theType, valuePtr) } -func (c *CoreFoundationLib) CFNumberGetValue(num uintptr, theType int64, valuePtr uintptr) bool { +func (c *CoreFoundationLib) CFNumberGetValue(num uintptr, theType int64, valuePtr unsafe.Pointer) bool { fn := getFunc[CFNumberGetValueFunc](c.library, "CFNumberGetValue") return fn(num, theType, valuePtr) } @@ -247,7 +269,9 @@ func (l *IOKitLib) IOObjectRelease(object uint32) int32 { return fn(object) } -func (l *IOKitLib) IOConnectCallStructMethod(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int32 { +func (l *IOKitLib) IOConnectCallStructMethod(connection, selector uint32, inputStruct unsafe.Pointer, inputStructCnt uintptr, + outputStruct unsafe.Pointer, outputStructCnt *uintptr, +) int32 { fn := getFunc[IOConnectCallStructMethodFunc](l.library, "IOConnectCallStructMethod") return fn(connection, selector, inputStruct, inputStructCnt, outputStruct, outputStructCnt) } @@ -294,14 +318,14 @@ func NewSystemLib() (*SystemLib, error) { return &SystemLib{library}, nil } -func (s *SystemLib) HostProcessorInfo(host uint32, flavor int32, outProcessorCount *uint32, outProcessorInfo uintptr, +func (s *SystemLib) HostProcessorInfo(host uint32, flavor int32, outProcessorCount *uint32, outProcessorInfo unsafe.Pointer, outProcessorInfoCnt *uint32, ) int32 { fn := getFunc[HostProcessorInfoFunc](s.library, "host_processor_info") return fn(host, flavor, outProcessorCount, outProcessorInfo, outProcessorInfoCnt) } -func (s *SystemLib) HostStatistics(host uint32, flavor int32, hostInfoOut uintptr, hostInfoOutCnt *uint32) int32 { +func (s *SystemLib) HostStatistics(host uint32, flavor int32, hostInfoOut unsafe.Pointer, hostInfoOutCnt *uint32) int32 { fn := getFunc[HostStatisticsFunc](s.library, "host_statistics") return fn(host, flavor, hostInfoOut, hostInfoOutCnt) } @@ -316,7 +340,7 @@ func (s *SystemLib) MachTaskSelf() uint32 { return fn() } -func (s *SystemLib) MachTimeBaseInfo(info uintptr) int32 { +func (s *SystemLib) MachTimeBaseInfo(info unsafe.Pointer) int32 { fn := getFunc[MachTimeBaseInfoFunc](s.library, "mach_timebase_info") return fn(info) } @@ -326,21 +350,51 @@ func (s *SystemLib) VMDeallocate(targetTask uint32, vmAddress, vmSize uintptr) i return fn(targetTask, vmAddress, vmSize) } -func (s *SystemLib) ProcPidPath(pid int32, buffer uintptr, bufferSize uint32) int32 { +func (s *SystemLib) ProcPidPath(pid int32, buffer unsafe.Pointer, bufferSize uint32) int32 { fn := getFunc[ProcPidPathFunc](s.library, "proc_pidpath") return fn(pid, buffer, bufferSize) } -func (s *SystemLib) ProcPidInfo(pid, flavor int32, arg uint64, buffer uintptr, bufferSize int32) int32 { +func (s *SystemLib) ProcPidInfo(pid, flavor int32, arg uint64, buffer unsafe.Pointer, bufferSize int32) int32 { fn := getFunc[ProcPidInfoFunc](s.library, "proc_pidinfo") return fn(pid, flavor, arg, buffer, bufferSize) } +func (s *SystemLib) ProcPidRusage(pid, flavor int32, buffer unsafe.Pointer) int32 { + fn := getFunc[ProcPidRusageFunc](s.library, "proc_pid_rusage") + return fn(pid, flavor, buffer) +} + +// ErrnoLocation returns a pointer to the calling OS thread's errno, as given by +// libc's __error(). The pointer is thread-local, so callers must hold +// runtime.LockOSThread() across this call, the libc call being checked, and the +// dereference of the returned pointer. +// +// Resolve the pointer *before* the libc call whose errno is to be inspected. +// Resolving a symbol performs a dlsym and allocates, and either may overwrite +// errno; doing it afterwards would race with the value being read. +func (s *SystemLib) ErrnoLocation() *int32 { + fn := getFunc[ErrnoFunc](s.library, "__error") + return fn() +} + // status codes const ( KERN_SUCCESS = 0 ) +// Arguments that point at Go memory are declared as unsafe.Pointer, a Go +// pointer type, or a slice -- never as uintptr. purego maps uintptr to +// uintptr_t, i.e. a plain integer: it neither keeps the pointee alive nor makes +// it escape, so a Go local stays on the goroutine stack and the address goes +// stale the moment the stack grows. Every such call then reads or writes the old +// stack and silently sees or produces zeroes. The other kinds are passed through +// reflect.Value.Pointer() and stay reachable for the duration of the call. +// +// uintptr remains correct for handles that are not Go memory: CoreFoundation and +// IOKit object references, mach ports, addresses of dylib data symbols obtained +// through Dlsym, and kernel-allocated vm addresses. + // IOKit types and constants. type ( IOServiceGetMatchingServiceFunc func(mainPort uint32, matching uintptr) uint32 @@ -355,7 +409,8 @@ type ( IORegistryEntryCreateCFPropertiesFunc func(entry uint32, properties unsafe.Pointer, allocator uintptr, options uint32) int32 IOObjectConformsToFunc func(object uint32, className string) bool IOObjectReleaseFunc func(object uint32) int32 - IOConnectCallStructMethodFunc func(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int32 + IOConnectCallStructMethodFunc func(connection, selector uint32, inputStruct unsafe.Pointer, inputStructCnt uintptr, + outputStruct unsafe.Pointer, outputStructCnt *uintptr) int32 IOHIDEventSystemClientCreateFunc func(allocator uintptr) unsafe.Pointer IOHIDEventSystemClientSetMatchingFunc func(client, match uintptr) int32 @@ -380,10 +435,14 @@ const ( ) // CoreFoundation types and constants. +// +// valuePtr on CFNumberCreate and CFNumberGetValue points at a caller-owned Go +// value that CoreFoundation reads from or writes into, hence unsafe.Pointer; see +// the note above the IOKit function types. type ( CFGetTypeIDFunc func(cf uintptr) int64 - CFNumberCreateFunc func(allocator uintptr, theType int64, valuePtr uintptr) unsafe.Pointer - CFNumberGetValueFunc func(num uintptr, theType int64, valuePtr uintptr) bool + CFNumberCreateFunc func(allocator uintptr, theType int64, valuePtr unsafe.Pointer) unsafe.Pointer + CFNumberGetValueFunc func(num uintptr, theType int64, valuePtr unsafe.Pointer) bool CFDictionaryCreateFunc func(allocator uintptr, keys, values *unsafe.Pointer, numValues int64, keyCallBacks, valueCallBacks uintptr) unsafe.Pointer CFDictionaryAddValueFunc func(theDict, key, value uintptr) @@ -413,13 +472,16 @@ type MachTimeBaseInfo struct { Denom uint32 } +// Buffers the kernel writes into are declared as unsafe.Pointer; see the note +// above the IOKit function types. vmAddress on VMDeallocateFunc stays a uintptr +// because it names kernel-allocated memory rather than Go memory. type ( - HostProcessorInfoFunc func(host uint32, flavor int32, outProcessorCount *uint32, outProcessorInfo uintptr, + HostProcessorInfoFunc func(host uint32, flavor int32, outProcessorCount *uint32, outProcessorInfo unsafe.Pointer, outProcessorInfoCnt *uint32) int32 - HostStatisticsFunc func(host uint32, flavor int32, hostInfoOut uintptr, hostInfoOutCnt *uint32) int32 + HostStatisticsFunc func(host uint32, flavor int32, hostInfoOut unsafe.Pointer, hostInfoOutCnt *uint32) int32 MachHostSelfFunc func() uint32 MachTaskSelfFunc func() uint32 - MachTimeBaseInfoFunc func(info uintptr) int32 + MachTimeBaseInfoFunc func(info unsafe.Pointer) int32 VMDeallocateFunc func(targetTask uint32, vmAddress, vmSize uintptr) int32 ) @@ -440,14 +502,18 @@ const ( ) type ( - ProcPidPathFunc func(pid int32, buffer uintptr, bufferSize uint32) int32 - ProcPidInfoFunc func(pid, flavor int32, arg uint64, buffer uintptr, bufferSize int32) int32 + ProcPidPathFunc func(pid int32, buffer unsafe.Pointer, bufferSize uint32) int32 + ProcPidInfoFunc func(pid, flavor int32, arg uint64, buffer unsafe.Pointer, bufferSize int32) int32 + ProcPidRusageFunc func(pid, flavor int32, buffer unsafe.Pointer) int32 + ErrnoFunc func() *int32 ) const ( - SysctlSym = "sysctl" - ProcPidPathSym = "proc_pidpath" - ProcPidInfoSym = "proc_pidinfo" + SysctlSym = "sysctl" + ProcPidPathSym = "proc_pidpath" + ProcPidInfoSym = "proc_pidinfo" + ProcPidRusageSym = "proc_pid_rusage" + ErrnoSym = "__error" ) const ( @@ -456,6 +522,10 @@ const ( PROC_PIDPATHINFO_MAXSIZE = 4 * MAXPATHLEN PROC_PIDTASKINFO = 4 PROC_PIDVNODEPATHINFO = 9 + + // RUSAGE_INFO_V2 is the proc_pid_rusage flavor that includes disk I/O bytes. + // See sys/resource.h rusage_info_v2. + RUSAGE_INFO_V2 = 2 ) // SMC represents a SMC instance. @@ -507,15 +577,18 @@ func NewSMC() (*SMC, error) { }, nil } -func (s *SMC) CallStruct(selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int32 { +func (s *SMC) CallStruct(selector uint32, inputStruct unsafe.Pointer, inputStructCnt uintptr, + outputStruct unsafe.Pointer, outputStructCnt *uintptr, +) int32 { return s.lib.IOConnectCallStructMethod(s.conn, selector, inputStruct, inputStructCnt, outputStruct, outputStructCnt) } +// Close releases the SMC connection. The IOKit handle itself is shared +// process-wide and is deliberately left open; see newLibrary. func (s *SMC) Close() error { if result := s.lib.IOServiceClose(s.conn); result != 0 { return errors.New("ERROR: IOServiceClose failed") } - s.lib.Close() return nil } @@ -537,10 +610,6 @@ func (s CStr) Ptr() *byte { return &s[0] } -func (s CStr) Addr() uintptr { - return uintptr(unsafe.Pointer(s.Ptr())) -} - func (s CStr) GoString() string { if s == nil { return "" diff --git a/vendor/github.com/shirou/gopsutil/v4/mem/mem_darwin.go b/vendor/github.com/shirou/gopsutil/v4/mem/mem_darwin.go index 1b3e9f21..58746a61 100644 --- a/vendor/github.com/shirou/gopsutil/v4/mem/mem_darwin.go +++ b/vendor/github.com/shirou/gopsutil/v4/mem/mem_darwin.go @@ -95,7 +95,7 @@ func VirtualMemoryWithContext(_ context.Context) (*VirtualMemoryStat, error) { var vmstat vmStatisticsData status := sys.HostStatistics(sys.MachHostSelf(), common.HOST_VM_INFO, - uintptr(unsafe.Pointer(&vmstat)), &count) + unsafe.Pointer(&vmstat), &count) if status != common.KERN_SUCCESS { return nil, fmt.Errorf("host_statistics error=%d", status) diff --git a/vendor/github.com/shirou/gopsutil/v4/mem/mem_linux.go b/vendor/github.com/shirou/gopsutil/v4/mem/mem_linux.go index a888c5ba..a33cd03b 100644 --- a/vendor/github.com/shirou/gopsutil/v4/mem/mem_linux.go +++ b/vendor/github.com/shirou/gopsutil/v4/mem/mem_linux.go @@ -479,7 +479,7 @@ func parseSwapsFile(ctx context.Context, r io.Reader) ([]*SwapDevice, error) { // Check header headerFields are as expected headerFields := strings.Fields(scanner.Text()) - if len(headerFields) < usedCol { + if len(headerFields) <= usedCol { return nil, fmt.Errorf("couldn't parse %q: too few fields in header", swapsFilePath) } if headerFields[nameCol] != "Filename" { @@ -495,7 +495,7 @@ func parseSwapsFile(ctx context.Context, r io.Reader) ([]*SwapDevice, error) { var swapDevices []*SwapDevice for scanner.Scan() { fields := strings.Fields(scanner.Text()) - if len(fields) < usedCol { + if len(fields) <= usedCol { return nil, fmt.Errorf("couldn't parse %q: too few fields", swapsFilePath) } diff --git a/vendor/github.com/shirou/gopsutil/v4/net/net_linux.go b/vendor/github.com/shirou/gopsutil/v4/net/net_linux.go index 96a78ff8..06c98d1f 100644 --- a/vendor/github.com/shirou/gopsutil/v4/net/net_linux.go +++ b/vendor/github.com/shirou/gopsutil/v4/net/net_linux.go @@ -50,6 +50,10 @@ func IOCountersByFileWithContext(_ context.Context, pernic bool, filename string return nil, err } + if len(lines) < 2 { + return nil, fmt.Errorf("malformed %s: expected at least 2 header lines, got %d", filename, len(lines)) + } + statlen := len(lines) - 1 ret := make([]IOCountersStat, 0, statlen) @@ -764,7 +768,7 @@ func processUnix(file string, kind netConnectionKindType, inodes map[string][]in // skip first line for _, line := range lines[1:] { tokens := strings.Fields(string(line)) - if len(tokens) < 6 { + if len(tokens) < 7 { continue } st, err := strconv.ParseInt(tokens[4], 10, 32) diff --git a/vendor/github.com/shirou/gopsutil/v4/net/net_windows.go b/vendor/github.com/shirou/gopsutil/v4/net/net_windows.go index f530e4e5..22dae714 100644 --- a/vendor/github.com/shirou/gopsutil/v4/net/net_windows.go +++ b/vendor/github.com/shirou/gopsutil/v4/net/net_windows.go @@ -365,7 +365,7 @@ func getTCPConnections(family uint32) ([]ConnectionStat, error) { return nil, errors.New("faimly must be required") } - for { + for retry := 0; retry < 10; retry++ { switch family { case kindTCP4.family: if len(buf) > 0 { @@ -395,6 +395,12 @@ func getTCPConnections(family uint32) ([]ConnectionStat, error) { if !errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) { return nil, err } + if retry == 9 { + return nil, errors.New("GetExtendedTcpTable: failed to allocate sufficient buffer after 10 retries") + } + + // Add 4KB padding to account for concurrent connection growth between calls. + size += 4096 buf = make([]byte, size) } @@ -446,7 +452,7 @@ func getUDPConnections(family uint32) ([]ConnectionStat, error) { return nil, errors.New("faimly must be required") } - for { + for retry := 0; retry < 10; retry++ { switch family { case kindUDP4.family: if len(buf) > 0 { @@ -478,6 +484,12 @@ func getUDPConnections(family uint32) ([]ConnectionStat, error) { if !errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) { return nil, err } + if retry == 9 { + return nil, errors.New("GetExtendedUdpTable: failed to allocate sufficient buffer after 10 retries") + } + + // Add 4KB padding to account for concurrent connection growth between calls. + size += 4096 buf = make([]byte, size) } diff --git a/vendor/github.com/shirou/gopsutil/v4/process/process.go b/vendor/github.com/shirou/gopsutil/v4/process/process.go index 5db5ff48..63b07e51 100644 --- a/vendor/github.com/shirou/gopsutil/v4/process/process.go +++ b/vendor/github.com/shirou/gopsutil/v4/process/process.go @@ -110,12 +110,14 @@ type IOCountersStat struct { // WriteCount is a number of read I/O operations such as syscalls. WriteCount uint64 `json:"writeCount"` // ReadBytes is a number of all I/O read in bytes. This includes disk I/O on Linux and Windows. + // Darwin does not expose this and reports 0; use DiskReadBytes there. ReadBytes uint64 `json:"readBytes"` - // WriteBytes is a number of all I/O write in bytes. This includes disk I/O on Linux and Windows. + // WriteBytes is a number of all I/O written in bytes. This includes disk I/O on Linux and Windows. + // Darwin does not expose this and reports 0; use DiskWriteBytes there. WriteBytes uint64 `json:"writeBytes"` - // DiskReadBytes is a number of disk I/O write in bytes. Currently only Linux has this value. + // DiskReadBytes is a number of disk I/O read in bytes. Currently, Linux and Darwin have this value. DiskReadBytes uint64 `json:"diskReadBytes"` - // DiskWriteBytes is a number of disk I/O read in bytes. Currently only Linux has this value. + // DiskWriteBytes is a number of disk I/O written in bytes. Currently, Linux and Darwin have this value. DiskWriteBytes uint64 `json:"diskWriteBytes"` } diff --git a/vendor/github.com/shirou/gopsutil/v4/process/process_darwin.go b/vendor/github.com/shirou/gopsutil/v4/process/process_darwin.go index 6e1d0ce3..6bb88d7e 100644 --- a/vendor/github.com/shirou/gopsutil/v4/process/process_darwin.go +++ b/vendor/github.com/shirou/gopsutil/v4/process/process_darwin.go @@ -7,7 +7,6 @@ import ( "bytes" "context" "encoding/binary" - "errors" "fmt" "path/filepath" "runtime" @@ -179,8 +178,72 @@ func (p *Process) NiceWithContext(_ context.Context) (int32, error) { return int32(k.Proc.P_nice), nil } -func (*Process) IOCountersWithContext(_ context.Context) (*IOCountersStat, error) { - return nil, common.ErrNotImplementedError +// rusageInfoV2 mirrors Darwin's struct rusage_info_v2 from sys/resource.h. +// Only DiskIOBytesRead / DiskIOBytesWritten are used for IOCounters. +type rusageInfoV2 struct { + UUID [16]byte + UserTime uint64 + SystemTime uint64 + PkgIdleWkups uint64 + InterruptWkups uint64 + Pageins uint64 + WiredSize uint64 + ResidentSize uint64 + PhysFootprint uint64 + ProcStartAbstime uint64 + ProcExitAbstime uint64 + ChildUserTime uint64 + ChildSystemTime uint64 + ChildPkgIdleWkups uint64 + ChildInterruptWkups uint64 + ChildPageins uint64 + ChildElapsedAbstime uint64 + DiskIOBytesRead uint64 + DiskIOBytesWritten uint64 +} + +// IOCountersWithContext returns cumulative disk I/O bytes for the process via +// proc_pid_rusage(RUSAGE_INFO_V2). +// +// Darwin only reports disk I/O, so only DiskReadBytes and DiskWriteBytes are +// populated. ReadBytes/WriteBytes count all I/O including that served from +// cache on other platforms, which has no Darwin equivalent, and ReadCount / +// WriteCount are not exposed at all; all four stay zero. +// +// Access may fail with ErrorNotPermitted for protected processes. +func (p *Process) IOCountersWithContext(_ context.Context) (*IOCountersStat, error) { + funcs, err := loadProcFuncs() + if err != nil { + return nil, err + } + defer funcs.Close() + + // Lock the OS thread so that errno, which is thread-local, still belongs to + // the thread that made the call below by the time it is read. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // Resolve the errno location up front: doing it after the call would run a + // dlsym and allocate in between, either of which can overwrite errno. + errnoPtr := funcs.lib.ErrnoLocation() + + var usage rusageInfoV2 + ret := funcs.lib.ProcPidRusage(p.Pid, common.RUSAGE_INFO_V2, unsafe.Pointer(&usage)) + if ret != 0 { + switch errno := unix.Errno(*errnoPtr); errno { + case unix.EPERM: + return nil, ErrorNotPermitted + case unix.ESRCH: + return nil, ErrorProcessNotRunning + default: + return nil, fmt.Errorf("proc_pid_rusage failed for pid %d: %w", p.Pid, errno) + } + } + + return &IOCountersStat{ + DiskReadBytes: usage.DiskIOBytesRead, + DiskWriteBytes: usage.DiskIOBytesWritten, + }, nil } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { @@ -294,7 +357,7 @@ func loadProcFuncs() (*dlFuncs, error) { func (f *dlFuncs) getTimeScaleToNanoSeconds() float64 { var timeBaseInfo common.MachTimeBaseInfo - f.lib.MachTimeBaseInfo(uintptr(unsafe.Pointer(&timeBaseInfo))) + f.lib.MachTimeBaseInfo(unsafe.Pointer(&timeBaseInfo)) return float64(timeBaseInfo.Numer) / float64(timeBaseInfo.Denom) } @@ -311,7 +374,7 @@ func (p *Process) ExeWithContext(_ context.Context) (string, error) { defer funcs.Close() buf := common.NewCStr(common.PROC_PIDPATHINFO_MAXSIZE) - ret := funcs.lib.ProcPidPath(p.Pid, buf.Addr(), common.PROC_PIDPATHINFO_MAXSIZE) + ret := funcs.lib.ProcPidPath(p.Pid, unsafe.Pointer(buf.Ptr()), common.PROC_PIDPATHINFO_MAXSIZE) if ret <= 0 { return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret) @@ -332,20 +395,24 @@ func (p *Process) CwdWithContext(_ context.Context) (string, error) { } defer funcs.Close() - // Lock OS thread to ensure the errno does not change + // Lock the OS thread so that errno, which is thread-local, still belongs to + // the thread that made the call below by the time it is read. runtime.LockOSThread() defer runtime.UnlockOSThread() + // Resolve the errno location up front: doing it after the call would run a + // dlsym and allocate in between, either of which can overwrite errno. + errnoPtr := funcs.lib.ErrnoLocation() + var vpi vnodePathInfo const vpiSize = int32(unsafe.Sizeof(vpi)) - ret := funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDVNODEPATHINFO, 0, uintptr(unsafe.Pointer(&vpi)), vpiSize) - errno, _ := funcs.lib.Dlsym("errno") - err = *(**unix.Errno)(unsafe.Pointer(&errno)) - if errors.Is(err, unix.EPERM) { - return "", ErrorNotPermitted - } - + ret := funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDVNODEPATHINFO, 0, unsafe.Pointer(&vpi), vpiSize) if ret <= 0 { + // errno is only meaningful once the call has reported failure; reading it + // unconditionally can surface a stale value from an earlier call. + if errno := unix.Errno(*errnoPtr); errno == unix.EPERM { + return "", ErrorNotPermitted + } return "", fmt.Errorf("unknown error: proc_pidinfo returned %d", ret) } @@ -450,7 +517,7 @@ func (p *Process) NumThreadsWithContext(_ context.Context) (int32, error) { defer funcs.Close() var ti ProcTaskInfo - funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti))) + funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, unsafe.Pointer(&ti), int32(unsafe.Sizeof(ti))) return int32(ti.Threadnum), nil } @@ -463,7 +530,7 @@ func (p *Process) TimesWithContext(_ context.Context) (*cpu.TimesStat, error) { defer funcs.Close() var ti ProcTaskInfo - funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti))) + funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, unsafe.Pointer(&ti), int32(unsafe.Sizeof(ti))) timescaleToNanoSeconds := funcs.getTimeScaleToNanoSeconds() ret := &cpu.TimesStat{ @@ -482,7 +549,7 @@ func (p *Process) MemoryInfoWithContext(_ context.Context) (*MemoryInfoStat, err defer funcs.Close() var ti ProcTaskInfo - funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti))) + funcs.lib.ProcPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, unsafe.Pointer(&ti), int32(unsafe.Sizeof(ti))) ret := &MemoryInfoStat{ RSS: uint64(ti.Resident_size), @@ -513,8 +580,8 @@ func (p *Process) NumFDsWithContext(_ context.Context) (int32, error) { p.Pid, common.PROC_PIDLISTFDS, 0, - 0, // NULL buffer - 0, // 0 size + nil, // NULL buffer + 0, // 0 size ) if bufferSize <= 0 { return 0, fmt.Errorf("unknown error: proc_pidinfo returned %d", bufferSize) @@ -530,8 +597,8 @@ func (p *Process) NumFDsWithContext(_ context.Context) (int32, error) { p.Pid, common.PROC_PIDLISTFDS, 0, - uintptr(unsafe.Pointer(&buf[0])), // Real buffer - bufferSize, // Size from first call + unsafe.Pointer(&buf[0]), // Real buffer + bufferSize, // Size from first call ) if ret <= 0 { return 0, fmt.Errorf("unknown error: proc_pidinfo returned %d", ret) diff --git a/vendor/github.com/shirou/gopsutil/v4/process/process_linux.go b/vendor/github.com/shirou/gopsutil/v4/process/process_linux.go index dc2f03bb..36d95d10 100644 --- a/vendor/github.com/shirou/gopsutil/v4/process/process_linux.go +++ b/vendor/github.com/shirou/gopsutil/v4/process/process_linux.go @@ -350,6 +350,9 @@ func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { continue } fields := splitProcStat(statContents) + if len(fields) < 5 { + continue + } pid, err := strconv.ParseInt(fields[1], 10, 32) if err != nil { continue @@ -756,6 +759,9 @@ func (p *Process) fillFromStatmWithContext(ctx context.Context) (*MemoryInfoStat return nil, nil, err } fields := strings.Split(string(contents), " ") + if len(fields) < 6 { + return nil, nil, fmt.Errorf("malformed statm file: expected at least 6 fields, got %d", len(fields)) + } vms, err := strconv.ParseUint(fields[0], 10, 64) if err != nil { @@ -861,6 +867,12 @@ func (p *Process) fillFromStatusWithContext(ctx context.Context) error { // Ensure we have a copy and not reference into slice p.name = string([]byte(p.name)) case "State": + if value == "" { + // Clear rather than leave a stale status from a previous + // parse, since Process objects can be reused across calls. + p.status = "" + continue + } p.status = convertStatusChar(value[0:1]) // Ensure we have a copy and not reference into slice p.status = string([]byte(p.status)) @@ -1195,6 +1207,12 @@ func readPidsFromDir(path string) ([]int32, error) { func splitProcStat(content []byte) []string { nameStart := bytes.IndexByte(content, '(') nameEnd := bytes.LastIndexByte(content, ')') + // Guard against a malformed/truncated stat file that is missing the + // parentheses around comm, which would otherwise cause the slice + // operations below to panic. Callers validate the field count. + if nameStart < 0 || nameEnd < nameStart || nameEnd+2 > len(content) { + return nil + } restFields := strings.Fields(string(content[nameEnd+2:])) // +2 skip ') ' name := content[nameStart+1 : nameEnd] pid := strings.TrimSpace(string(content[:nameStart])) diff --git a/vendor/github.com/shirou/gopsutil/v4/process/process_openbsd.go b/vendor/github.com/shirou/gopsutil/v4/process/process_openbsd.go index 31fdb85b..07257a85 100644 --- a/vendor/github.com/shirou/gopsutil/v4/process/process_openbsd.go +++ b/vendor/github.com/shirou/gopsutil/v4/process/process_openbsd.go @@ -8,6 +8,7 @@ import ( "context" "encoding/binary" "errors" + "fmt" "io" "path/filepath" "sort" @@ -94,6 +95,9 @@ func (p *Process) CmdlineSliceWithContext(_ context.Context) ([]string, error) { pointers followed by the strings themselves. The last char pointer is a NULL pointer. */ var strParts []string + if len(buf) == 0 { + return strParts, nil + } r := bytes.NewReader(buf) baseAddr := uintptr(unsafe.Pointer(&buf[0])) for { @@ -105,7 +109,17 @@ func (p *Process) CmdlineSliceWithContext(_ context.Context) ([]string, error) { break } offset := argvp - baseAddr - length := uintptr(bytes.IndexByte(buf[offset:], 0)) + // Reject a malformed/truncated sysctl reply whose pointers fall + // outside buf or whose strings are not NUL-terminated, which would + // otherwise cause the slice operations below to panic. + if offset >= uintptr(len(buf)) { + return nil, fmt.Errorf("malformed KERN_PROC_ARGV reply for pid %d: argv pointer out of bounds", p.Pid) + } + idx := bytes.IndexByte(buf[offset:], 0) + if idx < 0 { + return nil, fmt.Errorf("malformed KERN_PROC_ARGV reply for pid %d: argv string not NUL-terminated", p.Pid) + } + length := uintptr(idx) str := string(buf[offset : offset+length]) strParts = append(strParts, str) } diff --git a/vendor/github.com/sirupsen/logrus/.golangci.yml b/vendor/github.com/sirupsen/logrus/.golangci.yml index 792db361..c9a840e0 100644 --- a/vendor/github.com/sirupsen/logrus/.golangci.yml +++ b/vendor/github.com/sirupsen/logrus/.golangci.yml @@ -1,12 +1,9 @@ version: "2" -run: - tests: false linters: enable: - asasalint - asciicheck - bidichk - - bodyclose - contextcheck - durationcheck - errchkjson @@ -22,46 +19,21 @@ linters: - nilerr - nilnesserr - noctx - - protogetter - reassign - recvcheck - - rowserrcheck - - spancheck - - sqlclosecheck - testifylint - unparam - - zerologlint - disable: - - prealloc - settings: - errcheck: - check-type-assertions: false - check-blank: false - lll: - line-length: 100 - tab-width: 4 - prealloc: - simple: false - range-loops: false - for-loops: false - whitespace: - multi-if: false - multi-func: false exclusions: - generated: lax presets: - - comments - - common-false-positives - legacy - std-error-handling - paths: - - third_party$ - - builtin$ - - examples$ -formatters: - exclusions: - generated: lax - paths: - - third_party$ - - builtin$ - - examples$ + rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - gosec + - musttag + - noctx # TODO: enable once we switch to Go 1.24+. + - linters: # TODO: remove once golangci-lint is updated with https://github.com/golangci/golangci-lint/pull/6584 + - gocheckcompilerdirectives + text: 'compiler directive unrecognized: //go:fix' diff --git a/vendor/github.com/sirupsen/logrus/.travis.yml b/vendor/github.com/sirupsen/logrus/.travis.yml deleted file mode 100644 index c1dbd5a3..00000000 --- a/vendor/github.com/sirupsen/logrus/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: go -go_import_path: github.com/sirupsen/logrus -git: - depth: 1 -env: - - GO111MODULE=on -go: 1.15.x -os: linux -install: - - ./travis/install.sh -script: - - cd ci - - go run mage.go -v -w ../ crossBuild - - go run mage.go -v -w ../ lint - - go run mage.go -v -w ../ test diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md index 098608ff..3e667563 100644 --- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md +++ b/vendor/github.com/sirupsen/logrus/CHANGELOG.md @@ -1,90 +1,246 @@ -# 1.8.1 +# Changelog + +All notable changes to this project will be documented in this file. + +## 1.10.1 + +Fixes: + + * Fix a regression introduced in v1.10.0 where `TextFormatter` could panic + when formatting nil or panicking `error` and `fmt.Stringer` values. + * Allow function-backed implementations of `error` as field values. + +## 1.10.0 + +Fixes: + + * Fix reentrant logging deadlocks in formatter paths. + * Fix race conditions in formatter and entry handling. + * Fix generic `Log`, `Logf`, `Logln`, and `LogFn` methods unexpectedly + panicking when called with `PanicLevel`. Use the corresponding `Panic` + methods when panic behavior is desired. + * Improve concurrency safety around formatter and hook access. + +Features: + + * Add `slog` hook for forwarding Logrus entries to `log/slog`. + * Add `slog.Handler` for forwarding `log/slog` records to a Logrus logger, + including levels, fields, groups, context, time, and optional caller + reporting. The hook and handler can also be combined to help migrate + between Logrus and `log/slog`. + * Add minimal, composable logging interfaces for each log level. This enables + consumers to depend on narrower interfaces, making it easier to substitute + or adapt logging implementations. + * Allow `Entry.Caller` to be set explicitly and preserve it across derived + entries, enabling custom caller detection without Logrus overwriting + caller information when `ReportCaller` is enabled. + +Changed: + + * Raise minimum supported Go version to 1.23. + * TextFormatter now renders `[]byte` values as raw/quoted strings instead of slice-of-ints. + * TextFormatter now uses distinct dimmed colors for debug and trace output. + * TextFormatter now automatically enables colors on Windows terminals with ANSI support, + matching the behavior on other platforms. + * `Entry.HasCaller` is now deprecated in favor of checking `Entry.Caller` directly. + * Deprecated `MutexWrap`, which was unintentionally exposed as public API. + It remains available as an alias for compatibility but should not be used + directly. + +Performance: + + * Significantly improve TextFormatter performance and reduce allocations. + * Optimize common Entry and Logger hot paths. + * Reduce allocations in caller reporting. + * ~17% lower geomean runtime and ~27% higher formatter throughput overall. + * Common enabled logging paths are ~30–44% faster. + * TextFormatter paths are up to ~40% faster, with allocation counts reduced + by 25–74% across the measured formatter cases. + + +## 1.9.4 + +Fixes: + + * Remove uses of deprecated `ioutil` package + +Features: + + * Add GNU/Hurd support + * Add WASI wasip1 support + +Code quality: + + * Update minimum supported Go version to 1.17 + * Documentation updates + + +## 1.9.3 + +Fixes: + + * Re-apply fix for potential denial of service in logrus.Writer() when logging >64KB single-line payloads without newlines (#1376) + * Fix panic in Writer + + +## 1.9.2 + +Fixes: + + * Revert Writer DoS fix (#1376) due to regression + + +## 1.9.1 + +Fixes: + + * Fix potential denial of service in logrus.Writer() when logging >64KB single-line payloads without newlines (#1376) + + +## 1.9.0 + +Fixes: + + * Multiple concurrency and race condition fixes + * Improve Windows terminal and ANSI handling + +Code quality: + + * Internal cleanups and modernization + + +## 1.8.3 + +Fixes: + + * Fix potential denial of service in logrus.Writer() when logging >64KB single-line payloads without newlines (#1376) + + +## 1.8.2 + +Features: + + * Add support for the logger private buffer pool (#1253) + +Fixes: + + * Fix race condition for SetFormatter and SetReportCaller + * Fix data race in hooks test package + +## 1.8.1 + Code quality: + * move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer * improve timestamp format documentation Fixes: + * fix race condition on logger hooks -# 1.8.0 +## 1.8.0 Correct versioning number replacing v1.7.1. -# 1.7.1 +## 1.7.1 Beware this release has introduced a new public API and its semver is therefore incorrect. Code quality: + * use go 1.15 in travis * use magefile as task runner Fixes: + * small fixes about new go 1.13 error formatting system * Fix for long time race condiction with mutating data hooks Features: + * build support for zos -# 1.7.0 +## 1.7.0 + Fixes: + * the dependency toward a windows terminal library has been removed Features: + * a new buffer pool management API has been added * a set of `Fn()` functions have been added -# 1.6.0 +## 1.6.0 + Fixes: + * end of line cleanup * revert the entry concurrency bug fix which leads to deadlock under some circumstances * update dependency on go-windows-terminal-sequences to fix a crash with go 1.14 Features: + * add an option to the `TextFormatter` to completely disable fields quoting -# 1.5.0 +## 1.5.0 + Code quality: + * add golangci linter run on travis Fixes: + * add mutex for hooks concurrent access on `Entry` data * caller function field for go1.14 * fix build issue for gopherjs target Feature: + * add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level * add a `DisableHTMLEscape` option in the `JSONFormatter` * add `ForceQuote` and `PadLevelText` options in the `TextFormatter` -# 1.4.2 +## 1.4.2 + * Fixes build break for plan9, nacl, solaris -# 1.4.1 + +## 1.4.1 + This new release introduces: + * Enhance TextFormatter to not print caller information when they are empty (#944) * Remove dependency on golang.org/x/crypto (#932, #943) Fixes: + * Fix Entry.WithContext method to return a copy of the initial entry (#941) -# 1.4.0 +## 1.4.0 + This new release introduces: + * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848). * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911) * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919). Fixes: + * Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893). * Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903) * Fix infinite recursion on unknown `Level.String()` (#907) * Fix race condition in `getCaller` (#916). -# 1.3.0 +## 1.3.0 + This new release introduces: + * Log, Logf, Logln functions for Logger and Entry that take a Level Fixes: + * Building prometheus node_exporter on AIX (#840) * Race condition in TextFormatter (#468) * Travis CI import path (#868) @@ -92,20 +248,26 @@ Fixes: * Pointer to func as field in JSONFormatter (#870) * Properly marshal Levels (#873) -# 1.2.0 +## 1.2.0 + This new release introduces: + * A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued * A new trace level named `Trace` whose level is below `Debug` * A configurable exit function to be called upon a Fatal trace * The `Level` object now implements `encoding.TextUnmarshaler` interface -# 1.1.1 +## 1.1.1 + This is a bug fix release. + * fix the build break on Solaris * don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized -# 1.1.0 +## 1.1.0 + This new release introduces: + * several fixes: * a fix for a race condition on entry formatting * proper cleanup of previously used entries before putting them back in the pool @@ -122,9 +284,10 @@ This new release introduces: * the field sort function is now configurable for text formatter * the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater -# 1.0.6 +## 1.0.6 This new release introduces: + * a new api WithTime which allows to easily force the time of the log entry which is mostly useful for logger wrapper * a fix reverting the immutability of the entry given as parameter to the hooks @@ -134,71 +297,71 @@ This new release introduces: * a new configuration of the textformatter to configure the name of the default keys * a new configuration of the text formatter to disable the level truncation -# 1.0.5 +## 1.0.5 * Fix hooks race (#707) * Fix panic deadlock (#695) -# 1.0.4 +## 1.0.4 * Fix race when adding hooks (#612) * Fix terminal check in AppEngine (#635) -# 1.0.3 +## 1.0.3 * Replace example files with testable examples -# 1.0.2 +## 1.0.2 * bug: quote non-string values in text formatter (#583) * Make (*Logger) SetLevel a public method -# 1.0.1 +## 1.0.1 * bug: fix escaping in text formatter (#575) -# 1.0.0 +## 1.0.0 * Officially changed name to lower-case * bug: colors on Windows 10 (#541) * bug: fix race in accessing level (#512) -# 0.11.5 +## 0.11.5 * feature: add writer and writerlevel to entry (#372) -# 0.11.4 +## 0.11.4 * bug: fix undefined variable on solaris (#493) -# 0.11.3 +## 0.11.3 * formatter: configure quoting of empty values (#484) * formatter: configure quoting character (default is `"`) (#484) * bug: fix not importing io correctly in non-linux environments (#481) -# 0.11.2 +## 0.11.2 * bug: fix windows terminal detection (#476) -# 0.11.1 +## 0.11.1 * bug: fix tty detection with custom out (#471) -# 0.11.0 +## 0.11.0 * performance: Use bufferpool to allocate (#370) * terminal: terminal detection for app-engine (#343) * feature: exit handler (#375) -# 0.10.0 +## 0.10.0 * feature: Add a test hook (#180) * feature: `ParseLevel` is now case-insensitive (#326) * feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308) * performance: avoid re-allocations on `WithFields` (#335) -# 0.9.0 +## 0.9.0 * logrus/text_formatter: don't emit empty msg * logrus/hooks/airbrake: move out of main repository @@ -210,25 +373,25 @@ This new release introduces: * logrus/core: support `WithError` on logger * logrus/core: Solaris support -# 0.8.7 +## 0.8.7 * logrus/core: fix possible race (#216) * logrus/doc: small typo fixes and doc improvements -# 0.8.6 +## 0.8.6 * hooks/raven: allow passing an initialized client -# 0.8.5 +## 0.8.5 * logrus/core: revert #208 -# 0.8.4 +## 0.8.4 * formatter/text: fix data race (#218) -# 0.8.3 +## 0.8.3 * logrus/core: fix entry log level (#208) * logrus/core: improve performance of text formatter by 40% @@ -236,24 +399,24 @@ This new release introduces: * logrus/core: add support for DragonflyBSD and NetBSD * formatter/text: print structs more verbosely -# 0.8.2 +## 0.8.2 * logrus: fix more Fatal family functions -# 0.8.1 +## 0.8.1 * logrus: fix not exiting on `Fatalf` and `Fatalln` -# 0.8.0 +## 0.8.0 * logrus: defaults to stderr instead of stdout * hooks/sentry: add special field for `*http.Request` * formatter/text: ignore Windows for colors -# 0.7.3 +## 0.7.3 * formatter/\*: allow configuration of timestamp layout -# 0.7.2 +## 0.7.2 * formatter/text: Add configuration option for time format (#158) diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md index cc5dab7e..b2ff7aff 100644 --- a/vendor/github.com/sirupsen/logrus/README.md +++ b/vendor/github.com/sirupsen/logrus/README.md @@ -3,13 +3,10 @@ Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. -**Logrus is in maintenance-mode.** We will not be introducing new features. It's -simply too hard to do in a way that won't break many people's projects, which is -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). +**Logrus is in maintenance mode.** The project focuses on security, bug fixes, +and performance improvements. New features are not planned, aside from changes +required to provide interoperability with other logging ecosystems (e.g., Go's +[log/slog](https://pkg.go.dev/log/slog)). 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 @@ -23,18 +20,6 @@ about structured logging in Go today. Check out, for example, [zap]: https://github.com/uber-go/zap [apex]: https://github.com/apex/log -**Seeing weird case-sensitive problems?** It's in the past been possible to -import Logrus as both upper- and lower-case. Due to the Go package environment, -this caused issues in the community and we needed a standard. Some environments -experienced problems with the upper-case variant, so the lower-case was decided. -Everything using `logrus` will need to use the lower-case: -`github.com/sirupsen/logrus`. Any package that isn't, should be changed. - -To fix Glide, see [these -comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). -For an in-depth explanation of the casing issue, see [this -comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276). - Nicely color-coded in development (when a TTY is attached, otherwise just plain text): @@ -43,35 +28,27 @@ plain text): With `logrus.SetFormatter(&logrus.JSONFormatter{})`, for easy parsing by logstash or Splunk: -```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"} - -{"level":"warning","msg":"The group's number increased tremendously!", -"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} - -{"animal":"walrus","level":"info","msg":"A giant walrus appears!", -"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} - -{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.", -"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} - -{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true, -"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} +```json lines +{"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"} +{"level":"warning","msg":"The group's number increased tremendously!","number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} +{"animal":"walrus","level":"info","msg":"A giant walrus appears!","size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} +{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.","size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} +{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} ``` With the default `logrus.SetFormatter(&logrus.TextFormatter{})` when a TTY is not attached, the output is compatible with the [logfmt](https://pkg.go.dev/github.com/kr/logfmt) format: -```text +```bash time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8 time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4 time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009 -time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true +time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" animal=orca err="It's over 9000!" number=100 omg=true size=9009 ``` + To ensure this behaviour even if a TTY is attached, set your formatter as follows: ```go @@ -88,29 +65,32 @@ If you wish to add the calling method as a field, instruct the logger via: ```go logrus.SetReportCaller(true) ``` + This adds the caller as 'method' like so: ```json -{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by", -"time":"2014-03-10 19:57:38.562543129 -0400 EDT"} +{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by","time":"2014-03-10 19:57:38.562543129 -0400 EDT"} ``` -```text +```bash time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin ``` + 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: ```bash -go test -bench=.*CallerTracing +go test -bench=ReportCaller ``` #### Case-sensitivity -The organization's name was changed to lower-case--and this will not be changed -back. If you are getting import conflicts due to case sensitivity, please use -the lower-case import: `github.com/sirupsen/logrus`. +The organization's name was [changed to lower-case][1]. If you are getting import +conflicts due to case sensitivity, please use the lower-case import: +`github.com/sirupsen/logrus`. + +[1]: https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276 #### Example @@ -289,6 +269,7 @@ func init() { } } ``` + Note: Syslog hooks also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) @@ -367,18 +348,21 @@ Splunk or Logstash. The built-in logging formatters are: -* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise - without colors. - * *Note:* to force colored output when there is no TTY, set the `ForceColors` +* [`logrus.TextFormatter`](https://pkg.go.dev/github.com/sirupsen/logrus#TextFormatter) + logs the event in colors if the logger output is a TTY, otherwise without colors. + * To force colored output when there is no TTY, set the `ForceColors` field to `true`. To force no colored output even if there is a TTY set the - `DisableColors` field to `true`. For Windows, see - [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable). + `DisableColors` field to `true`. + * On modern Windows terminals with ANSI (Virtual Terminal) support, TextFormatter + automatically enables colored output. + * If your environment does not support ANSI escape sequences, wrap the logger output + using [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable) + and set `ForceColors` (or `CLICOLOR_FORCE=1`) to enable colors through the wrapper. * When colors are enabled, levels are truncated to 4 characters by default. To disable truncation set the `DisableLevelTruncation` field to `true`. * When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text. - * All options are listed in the [generated docs](https://pkg.go.dev/github.com/sirupsen/logrus#TextFormatter). -* `logrus.JSONFormatter`. Logs fields as JSON. - * All options are listed in the [generated docs](https://pkg.go.dev/github.com/sirupsen/logrus#JSONFormatter). +* [`logrus.JSONFormatter`](https://pkg.go.dev/github.com/sirupsen/logrus#JSONFormatter) + logs fields as JSON. Third-party logging formatters: @@ -390,10 +374,12 @@ Third-party logging formatters: * [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure. * [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Save log to files. * [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added. +* [`easy-logrus-formatter`](https://github.com/WeiZhixiong/easy-logrus-formatter). Provide a user-friendly formatter for logrus. +* [`redactrus`](https://github.com/ibreakthecloud/redactrus). Redacts sensitive information like password, apikeys, email, etc. from logs. You can define your formatter by implementing the `Formatter` interface, requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a -`Fields` type (`map[string]interface{}`) with all your fields as well as the +`Fields` type (`map[string]any`) with all your fields as well as the default ones (see Entries section above): ```go @@ -516,4 +502,4 @@ Situations when locking is not needed include: 2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing) - (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) + (Refer to ) diff --git a/vendor/github.com/sirupsen/logrus/alt_exit.go b/vendor/github.com/sirupsen/logrus/alt_exit.go index 8fd189e1..1c35cf81 100644 --- a/vendor/github.com/sirupsen/logrus/alt_exit.go +++ b/vendor/github.com/sirupsen/logrus/alt_exit.go @@ -57,7 +57,7 @@ func Exit(code int) { // // This method is useful when a caller wishes to use logrus to log a fatal // message but also needs to gracefully shutdown. An example usecase could be -// closing database connections, or sending a alert that the application is +// closing database connections, or sending an alert that the application is // closing. func RegisterExitHandler(handler func()) { handlers = append(handlers, handler) @@ -69,7 +69,7 @@ func RegisterExitHandler(handler func()) { // // This method is useful when a caller wishes to use logrus to log a fatal // message but also needs to gracefully shutdown. An example usecase could be -// closing database connections, or sending a alert that the application is +// closing database connections, or sending an alert that the application is // closing. func DeferExitHandler(handler func()) { handlers = append([]func(){handler}, handlers...) diff --git a/vendor/github.com/sirupsen/logrus/buffer_pool.go b/vendor/github.com/sirupsen/logrus/buffer_pool.go index c7787f77..6b562d87 100644 --- a/vendor/github.com/sirupsen/logrus/buffer_pool.go +++ b/vendor/github.com/sirupsen/logrus/buffer_pool.go @@ -5,9 +5,13 @@ import ( "sync" ) -var ( - bufferPool BufferPool -) +var bufferPool BufferPool = &defaultPool{ + pool: &sync.Pool{ + New: func() any { + return new(bytes.Buffer) + }, + }, +} type BufferPool interface { Put(*bytes.Buffer) @@ -27,17 +31,7 @@ func (p *defaultPool) Get() *bytes.Buffer { } // SetBufferPool allows to replace the default logrus buffer pool -// to better meets the specific needs of an application. +// to better meet the specific needs of an application. func SetBufferPool(bp BufferPool) { bufferPool = bp } - -func init() { - SetBufferPool(&defaultPool{ - pool: &sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, - }, - }) -} diff --git a/vendor/github.com/sirupsen/logrus/doc.go b/vendor/github.com/sirupsen/logrus/doc.go index da67aba0..75186dc2 100644 --- a/vendor/github.com/sirupsen/logrus/doc.go +++ b/vendor/github.com/sirupsen/logrus/doc.go @@ -1,25 +1,25 @@ /* Package logrus is a structured logger for Go, completely API compatible with the standard library logger. - The simplest way to use Logrus is simply the package-level exported logger: - package main + package main - import ( - log "github.com/sirupsen/logrus" - ) + import ( + log "github.com/sirupsen/logrus" + ) - func main() { - log.WithFields(log.Fields{ - "animal": "walrus", - "number": 1, - "size": 10, - }).Info("A walrus appears") - } + func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "number": 1, + "size": 10, + }).Info("A walrus appears") + } Output: - time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 + + time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 For a full guide visit https://github.com/sirupsen/logrus */ diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go index 71d796d0..82de41f9 100644 --- a/vendor/github.com/sirupsen/logrus/entry.go +++ b/vendor/github.com/sirupsen/logrus/entry.go @@ -4,9 +4,11 @@ import ( "bytes" "context" "fmt" + "maps" "os" "reflect" "runtime" + "strconv" "strings" "sync" "time" @@ -17,8 +19,10 @@ var ( // qualified package name, cached at first use logrusPackage string - // Positions in the call stack when tracing to report the calling method - minimumCallerDepth int + // Positions in the call stack when tracing to report the calling method. + // + // Start at the bottom of the stack before the package-name cache is primed. + minimumCallerDepth = 1 // Used for caller information initialisation callerInitOnce sync.Once @@ -29,68 +33,116 @@ const ( knownLogrusFrames int = 4 ) -func init() { - // start at the bottom of the stack before the package-name cache is primed - minimumCallerDepth = 1 -} - // ErrorKey defines the key when adding errors using [WithError], [Logger.WithError]. var ErrorKey = "error" -// Entry is the final or intermediate Logrus logging entry. It contains all -// the fields passed with WithField{,s}. It's finally logged when Trace, Debug, -// Info, Warn, Error, Fatal or Panic is called on it. These objects can be -// reused and passed around as much as you wish to avoid field duplication. +// Entry represents a single log event. It may be either an intermediate +// entry (created via WithField(s), WithContext, etc.) or a final entry +// that is emitted when one of the level methods (Trace, Debug, Info, +// Warn, Error, Fatal, Panic) is called. // -//nolint:recvcheck // the methods of "Entry" use pointer receiver and non-pointer receiver. +// An Entry always belongs to a Logger. A nil Logger is invalid and will +// cause a panic when the entry is logged. Use [NewEntry] or Logger methods +// to construct entries. +// +// Entries are safe to reuse for adding fields and may be passed around +// to avoid field duplication. Each log operation operates on a copy +// of the Entry’s data to avoid mutation during formatting. +// +//nolint:recvcheck // Entry methods intentionally use both pointer and value receivers. type Entry struct { + // Logger is the Logger that owns this entry and is responsible for + // formatting, hooks, and output. It must not be nil. An Entry without + // a Logger is invalid and will panic when logged. Logger *Logger - // Contains all the fields set by the user. + // Data contains all user-defined fields attached to this entry. Data Fields - // Time at which the log entry was created + // Time is the timestamp for the log event. If zero when the entry is + // logged, it defaults to the current time. Time time.Time - // Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic - // This field will be set on entry firing and the value will be equal to the one in Logger struct field. + // Level is the severity of the log entry. It is set when the entry + // is fired and reflects the level used for that log call. Level Level - // Calling method, with package name + // Caller contains the calling method information. + // + // When [Logger.ReportCaller] is enabled, Caller is populated automatically at + // log time if it is nil. Hooks and formatters may inspect Caller. + // + // Applications generally should not modify Caller unless they intentionally + // want to provide custom caller information. Caller *runtime.Frame - // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic + // Message is the log message supplied to one of the logging methods + // (Trace, Debug, Info, Warn, Error, Fatal, or Panic). It is set when + // the entry is logged. Message string - // When formatter is called in entry.log(), a Buffer may be set to entry + // Buffer is a reusable buffer provided to the formatter. It is set + // before formatting in the normal log path; when nil, formatters + // allocate their own. Buffer *bytes.Buffer - // Contains the context set by the user. Useful for hook processing etc. + // Context carries user-provided context for hooks and formatters. Context context.Context - // err may contain a field formatting error + // err contains internal field-formatting errors. err string } +// NewEntry creates a new [Entry] associated with the provided Logger. +// The logger must not be nil. Passing a nil logger results in a +// panic when a logging method (e.g., [Entry.Info], [Entry.Error], etc.) +// is called. func NewEntry(logger *Logger) *Entry { return &Entry{ Logger: logger, - // Default is three fields, plus one optional. Give a little extra room. - Data: make(Fields, 6), + // Reserve default predefined fields and a little extra room. + Data: make(Fields, defaultFields+3), } } +// Dup creates a copy of the entry for further modification. +// +// Data is cloned to avoid mutating the original entry. Other fields +// (Logger, Time, Context, etc.) are copied by value. func (entry *Entry) Dup() *Entry { - data := make(Fields, len(entry.Data)) - for k, v := range entry.Data { - data[k] = v + dup := entry.dup() + dup.Data = maps.Clone(entry.Data) + return dup +} + +// dup copies the entry fields shared by derived entries except Data, which +// callers must copy or initialize as appropriate for their use. +func (entry *Entry) dup() *Entry { + return &Entry{ + Logger: entry.Logger, + Time: entry.Time, + Caller: entry.Caller, + Context: entry.Context, + err: entry.err, } - return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err} } // Bytes returns the bytes representation of this entry from the formatter. func (entry *Entry) Bytes() ([]byte, error) { - return entry.Logger.Formatter.Format(entry) + // Snapshot the formatter under the lock to protect against concurrent + // SetFormatter calls, then release the lock before formatting. + // This avoids a data race and prevents a deadlock if Format() triggers + // reentrant logging (e.g., a field's MarshalJSON calls logrus). + // + // See: + // + // - https://github.com/sirupsen/logrus/issues/1440 + // - https://github.com/sirupsen/logrus/issues/1448 + entry.Logger.mu.Lock() + formatter := entry.Logger.Formatter + entry.Logger.mu.Unlock() + + return formatter.Format(entry) } // String returns the string representation from the reader and ultimately the @@ -112,58 +164,61 @@ func (entry *Entry) WithError(err error) *Entry { // WithContext adds a context to the Entry. func (entry *Entry) WithContext(ctx context.Context) *Entry { - dataCopy := make(Fields, len(entry.Data)) - for k, v := range entry.Data { - dataCopy[k] = v - } - return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx} + dup := entry.dup() + dup.Data = maps.Clone(entry.Data) + dup.Context = ctx + return dup } // WithField adds a single field to the Entry. -func (entry *Entry) WithField(key string, value interface{}) *Entry { - return entry.WithFields(Fields{key: value}) +func (entry *Entry) WithField(key string, value any) *Entry { + dup := entry.dup() + dup.Data = maps.Clone(entry.Data) + dup.addField(key, value) + return dup } // WithFields adds a map of fields to the Entry. func (entry *Entry) WithFields(fields Fields) *Entry { - data := make(Fields, len(entry.Data)+len(fields)) - for k, v := range entry.Data { - data[k] = v + dup := entry.dup() + dup.Data = make(Fields, len(entry.Data)+len(fields)) + maps.Copy(dup.Data, entry.Data) + + for key, value := range fields { + dup.addField(key, value) } - fieldErr := entry.err - for k, v := range fields { - isErrField := false - if t := reflect.TypeOf(v); t != nil { - switch { - case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func: - isErrField = true - } - } - if isErrField { - tmp := fmt.Sprintf("can not add field %q", k) - if fieldErr != "" { - fieldErr = entry.err + ", " + tmp + return dup +} + +// WithTime overrides the time of the Entry. +func (entry *Entry) WithTime(t time.Time) *Entry { + dup := entry.dup() + dup.Data = maps.Clone(entry.Data) + dup.Time = t + return dup +} + +func (entry *Entry) addField(key string, value any) { + if _, ok := value.(error); !ok { + t := reflect.TypeOf(value) + if t != nil && (t.Kind() == reflect.Func || t.Kind() == reflect.Pointer && t.Elem().Kind() == reflect.Func) { + if entry.err != "" { + entry.err += ", skipping unsupported field " + strconv.Quote(key) } else { - fieldErr = tmp + entry.err = "skipping unsupported field " + strconv.Quote(key) } - } else { - data[k] = v + return } } - return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context} -} -// WithTime overrides the time of the Entry. -func (entry *Entry) WithTime(t time.Time) *Entry { - dataCopy := make(Fields, len(entry.Data)) - for k, v := range entry.Data { - dataCopy[k] = v + if entry.Data == nil { + entry.Data = make(Fields, 1) } - return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context} + entry.Data[key] = value } // getPackageName reduces a fully qualified function name to the package name -// There really ought to be to be a better way... +// There really ought to be a better way... func getPackageName(f string) string { for { lastPeriod := strings.LastIndex(f, ".") @@ -186,7 +241,7 @@ func getCaller() *runtime.Frame { _ = runtime.Callers(0, pcs) // dynamic get the package name and the minimum caller depth - for i := 0; i < maximumCallerDepth; i++ { + for i := range maximumCallerDepth { funcName := runtime.FuncForPC(pcs[i]).Name() if strings.Contains(funcName, "getCaller") { logrusPackage = getPackageName(funcName) @@ -215,16 +270,47 @@ func getCaller() *runtime.Frame { return nil } -func (entry Entry) HasCaller() (has bool) { - return entry.Logger != nil && - entry.Logger.ReportCaller && - entry.Caller != nil +// HasCaller reports whether this Entry contains caller information. +// +// Caller may be set explicitly, or populated at log time when +// [Logger.ReportCaller] is enabled. +// +// Deprecated: use [Entry.Caller] != nil instead. +// +//go:fix inline +func (entry Entry) HasCaller() bool { + return entry.Caller != nil } -func (entry *Entry) log(level Level, msg string) { - var buffer *bytes.Buffer +func (entry *Entry) logArgs(level Level, panicAfter bool, args ...any) { + entry.log(level, panicAfter, sprint(args...)) +} - newEntry := entry.Dup() +func (entry *Entry) logf(level Level, panicAfter bool, format string, args ...any) { + entry.log(level, panicAfter, fmt.Sprintf(format, args...)) +} + +// logln uses Sprintln for multiple arguments to preserve Println-style +// spacing between args, then trims the trailing newline. +func (entry *Entry) logln(level Level, panicAfter bool, args ...any) { + if len(args) <= 1 { + entry.log(level, panicAfter, sprint(args...)) + return + } + msg := fmt.Sprintln(args...) + msg = msg[:len(msg)-1] // Trim the newline added by Sprintln; logging adds its own. + entry.log(level, panicAfter, msg) +} + +// log writes msg at level. If panicAfter is true, it panics with the fully +// populated entry after hooks and output have completed. +// +// The explicit flag keeps panic behavior limited to Panic, Panicf, and +// Panicln while avoiding a return value used only as the panic value. +// See #1283 and commits f96066e and 5f8c666. +func (entry *Entry) log(level Level, panicAfter bool, msg string) { + newEntry := entry.dup() + newEntry.Data = maps.Clone(entry.Data) if newEntry.Time.IsZero() { newEntry.Time = time.Now() @@ -233,17 +319,24 @@ func (entry *Entry) log(level Level, msg string) { newEntry.Level = level newEntry.Message = msg - newEntry.Logger.mu.Lock() - reportCaller := newEntry.Logger.ReportCaller + logger := newEntry.Logger + logger.mu.Lock() + reportCaller := logger.ReportCaller bufPool := newEntry.getBufferPool() - newEntry.Logger.mu.Unlock() + logger.mu.Unlock() - if reportCaller { + // Preserve explicitly set caller information. + if reportCaller && newEntry.Caller == nil { newEntry.Caller = getCaller() } - newEntry.fireHooks() - buffer = bufPool.Get() + // Select hooks based on the level for this log call. Hooks receive the + // Entry and may mutate it, but that does not affect which hooks are + // fired for this event. + hooks := logger.hooksForLevel(level) + newEntry.fireHooks(hooks) + + buffer := bufPool.Get() defer func() { newEntry.Buffer = nil buffer.Reset() @@ -251,15 +344,12 @@ func (entry *Entry) log(level Level, msg string) { }() buffer.Reset() newEntry.Buffer = buffer - newEntry.write() - newEntry.Buffer = nil - // To avoid Entry#log() returning a value that only would make sense for - // panic() to use in Entry#Panic(), we avoid the allocation by checking - // directly here. - if level <= PanicLevel { + // Panic here so the panic value contains the fully populated entry without + // requiring log to return it to the caller. + if panicAfter { panic(newEntry) } } @@ -271,175 +361,207 @@ func (entry *Entry) getBufferPool() (pool BufferPool) { return bufferPool } -func (entry *Entry) fireHooks() { - var tmpHooks LevelHooks - entry.Logger.mu.Lock() - tmpHooks = make(LevelHooks, len(entry.Logger.Hooks)) - for k, v := range entry.Logger.Hooks { - tmpHooks[k] = v - } - entry.Logger.mu.Unlock() - - err := tmpHooks.Fire(entry.Level, entry) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) +func (entry *Entry) fireHooks(hooks []Hook) { + for _, hook := range hooks { + if err := hook.Fire(entry); err != nil { + _, _ = fmt.Fprintln(os.Stderr, "Failed to fire hook:", err) + return + } } } func (entry *Entry) write() { + // Snapshot the formatter under the lock to protect against concurrent + // SetFormatter calls, then release the lock before formatting. + // This avoids a deadlock when Format() triggers reentrant logging (e.g., + // a field's MarshalJSON calls logrus). See #1448, #1440. entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() - serialized, err := entry.Logger.Formatter.Format(entry) + formatter := entry.Logger.Formatter + entry.Logger.mu.Unlock() + + serialized, err := formatter.Format(entry) if err != nil { - fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) + _, _ = fmt.Fprintln(os.Stderr, "Failed to format entry:", err) return } + + // Re-acquire the lock to serialize writes to the underlying io.Writer. + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() if _, err := entry.Logger.Out.Write(serialized); err != nil { - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + _, _ = fmt.Fprintln(os.Stderr, "Failed to write to log:", err) } } -// Log will log a message at the level given as parameter. -// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit. -// For this behaviour Entry.Panic or Entry.Fatal should be used instead. -func (entry *Entry) Log(level Level, args ...interface{}) { +// Log logs a message at the specified level. +// +// Using Log with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Log treats the level as logging severity only; +// use [Entry.Panic] or [Entry.Fatal] when those side effects are desired. +func (entry *Entry) Log(level Level, args ...any) { + const panicAfter = false if entry.Logger.IsLevelEnabled(level) { - entry.log(level, fmt.Sprint(args...)) + entry.logArgs(level, panicAfter, args...) } } -func (entry *Entry) Trace(args ...interface{}) { +func (entry *Entry) Trace(args ...any) { entry.Log(TraceLevel, args...) } -func (entry *Entry) Debug(args ...interface{}) { +func (entry *Entry) Debug(args ...any) { entry.Log(DebugLevel, args...) } -func (entry *Entry) Print(args ...interface{}) { +func (entry *Entry) Print(args ...any) { entry.Info(args...) } -func (entry *Entry) Info(args ...interface{}) { +func (entry *Entry) Info(args ...any) { entry.Log(InfoLevel, args...) } -func (entry *Entry) Warn(args ...interface{}) { +func (entry *Entry) Warn(args ...any) { entry.Log(WarnLevel, args...) } -func (entry *Entry) Warning(args ...interface{}) { +func (entry *Entry) Warning(args ...any) { entry.Warn(args...) } -func (entry *Entry) Error(args ...interface{}) { +func (entry *Entry) Error(args ...any) { entry.Log(ErrorLevel, args...) } -func (entry *Entry) Fatal(args ...interface{}) { +func (entry *Entry) Fatal(args ...any) { entry.Log(FatalLevel, args...) entry.Logger.Exit(1) } -func (entry *Entry) Panic(args ...interface{}) { - entry.Log(PanicLevel, args...) +func (entry *Entry) Panic(args ...any) { + const panicAfter = true + if entry.Logger.IsLevelEnabled(PanicLevel) { + entry.logArgs(PanicLevel, panicAfter, args...) + } } // Entry Printf family functions -func (entry *Entry) Logf(level Level, format string, args ...interface{}) { +// Logf logs a formatted message at the specified level. +// +// Using Logf with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Logf treats the level as logging severity only; +// use [Entry.Panicf] or [Entry.Fatalf] when those side effects are desired. +func (entry *Entry) Logf(level Level, format string, args ...any) { + const panicAfter = false if entry.Logger.IsLevelEnabled(level) { - entry.Log(level, fmt.Sprintf(format, args...)) + entry.logf(level, panicAfter, format, args...) } } -func (entry *Entry) Tracef(format string, args ...interface{}) { +func (entry *Entry) Tracef(format string, args ...any) { entry.Logf(TraceLevel, format, args...) } -func (entry *Entry) Debugf(format string, args ...interface{}) { +func (entry *Entry) Debugf(format string, args ...any) { entry.Logf(DebugLevel, format, args...) } -func (entry *Entry) Infof(format string, args ...interface{}) { +func (entry *Entry) Infof(format string, args ...any) { entry.Logf(InfoLevel, format, args...) } -func (entry *Entry) Printf(format string, args ...interface{}) { +func (entry *Entry) Printf(format string, args ...any) { entry.Infof(format, args...) } -func (entry *Entry) Warnf(format string, args ...interface{}) { +func (entry *Entry) Warnf(format string, args ...any) { entry.Logf(WarnLevel, format, args...) } -func (entry *Entry) Warningf(format string, args ...interface{}) { +func (entry *Entry) Warningf(format string, args ...any) { entry.Warnf(format, args...) } -func (entry *Entry) Errorf(format string, args ...interface{}) { +func (entry *Entry) Errorf(format string, args ...any) { entry.Logf(ErrorLevel, format, args...) } -func (entry *Entry) Fatalf(format string, args ...interface{}) { +func (entry *Entry) Fatalf(format string, args ...any) { entry.Logf(FatalLevel, format, args...) entry.Logger.Exit(1) } -func (entry *Entry) Panicf(format string, args ...interface{}) { - entry.Logf(PanicLevel, format, args...) +func (entry *Entry) Panicf(format string, args ...any) { + const panicAfter = true + if entry.Logger.IsLevelEnabled(PanicLevel) { + entry.logf(PanicLevel, panicAfter, format, args...) + } } // Entry Println family functions -func (entry *Entry) Logln(level Level, args ...interface{}) { +// Logln logs a message at the specified level with Println-style spacing. +// +// Using Logln with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Logln treats the level as logging severity only; +// use [Entry.Panicln] or [Entry.Fatalln] when those side effects are desired. +func (entry *Entry) Logln(level Level, args ...any) { + const panicAfter = false if entry.Logger.IsLevelEnabled(level) { - entry.Log(level, entry.sprintlnn(args...)) + entry.logln(level, panicAfter, args...) } } -func (entry *Entry) Traceln(args ...interface{}) { +func (entry *Entry) Traceln(args ...any) { entry.Logln(TraceLevel, args...) } -func (entry *Entry) Debugln(args ...interface{}) { +func (entry *Entry) Debugln(args ...any) { entry.Logln(DebugLevel, args...) } -func (entry *Entry) Infoln(args ...interface{}) { +func (entry *Entry) Infoln(args ...any) { entry.Logln(InfoLevel, args...) } -func (entry *Entry) Println(args ...interface{}) { +func (entry *Entry) Println(args ...any) { entry.Infoln(args...) } -func (entry *Entry) Warnln(args ...interface{}) { +func (entry *Entry) Warnln(args ...any) { entry.Logln(WarnLevel, args...) } -func (entry *Entry) Warningln(args ...interface{}) { +func (entry *Entry) Warningln(args ...any) { entry.Warnln(args...) } -func (entry *Entry) Errorln(args ...interface{}) { +func (entry *Entry) Errorln(args ...any) { entry.Logln(ErrorLevel, args...) } -func (entry *Entry) Fatalln(args ...interface{}) { +func (entry *Entry) Fatalln(args ...any) { entry.Logln(FatalLevel, args...) entry.Logger.Exit(1) } -func (entry *Entry) Panicln(args ...interface{}) { - entry.Logln(PanicLevel, args...) +func (entry *Entry) Panicln(args ...any) { + const panicAfter = true + if entry.Logger.IsLevelEnabled(PanicLevel) { + entry.logln(PanicLevel, panicAfter, args...) + } } -// sprintlnn => Sprint no newline. This is to get the behavior of how -// fmt.Sprintln where spaces are always added between operands, regardless of -// their type. Instead of vendoring the Sprintln implementation to spare a -// string allocation, we do the simplest thing. -func (entry *Entry) sprintlnn(args ...interface{}) string { - msg := fmt.Sprintln(args...) - return msg[:len(msg)-1] +// sprint is fmt.Sprint with fast paths for zero or one string argument. +func sprint(args ...any) string { + switch len(args) { + case 0: + return "" + case 1: + if msg, ok := args[0].(string); ok { + return msg + } + } + return fmt.Sprint(args...) } diff --git a/vendor/github.com/sirupsen/logrus/exported.go b/vendor/github.com/sirupsen/logrus/exported.go index 017c30ce..8b261c12 100644 --- a/vendor/github.com/sirupsen/logrus/exported.go +++ b/vendor/github.com/sirupsen/logrus/exported.go @@ -6,11 +6,12 @@ import ( "time" ) -var ( - // std is the name of the standard logger in stdlib `log` - std = New() -) +// std is the package-level standard logger, similar to the default logger +// in the stdlib [log] package. +var std = New() +// StandardLogger returns the package-level standard logger used by +// the top-level logging functions. func StandardLogger() *Logger { return std } @@ -41,7 +42,7 @@ func GetLevel() Level { return std.GetLevel() } -// IsLevelEnabled checks if the log level of the standard logger is greater than the level param +// IsLevelEnabled checks if logging for the given level is enabled for the standard logger. func IsLevelEnabled(level Level) bool { return std.IsLevelEnabled(level) } @@ -51,9 +52,10 @@ func AddHook(hook Hook) { std.AddHook(hook) } -// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. +// WithError creates an entry from the standard logger and adds an error to it, +// using the value defined in [ErrorKey] as key. func WithError(err error) *Entry { - return std.WithField(ErrorKey, err) + return std.WithError(err) } // WithContext creates an entry from the standard logger and adds a context to it. @@ -61,210 +63,203 @@ func WithContext(ctx context.Context) *Entry { return std.WithContext(ctx) } -// WithField creates an entry from the standard logger and adds a field to -// it. If you want multiple fields, use `WithFields`. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. -func WithField(key string, value interface{}) *Entry { +// WithField creates an entry from the standard logger and adds a single field. +// For multiple fields, prefer [WithFields] over chaining WithField calls. +func WithField(key string, value any) *Entry { return std.WithField(key, value) } -// WithFields creates an entry from the standard logger and adds multiple -// fields to it. This is simply a helper for `WithField`, invoking it -// once for each field. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. +// WithFields creates an entry from the standard logger and adds the fields to it. func WithFields(fields Fields) *Entry { return std.WithFields(fields) } -// WithTime creates an entry from the standard logger and overrides the time of -// logs generated with it. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. +// WithTime creates an entry from the standard logger and overrides the time +// used for logs generated with it. func WithTime(t time.Time) *Entry { return std.WithTime(t) } -// Trace logs a message at level Trace on the standard logger. -func Trace(args ...interface{}) { +// Trace logs a message at level [TraceLevel] on the standard logger. +func Trace(args ...any) { std.Trace(args...) } -// Debug logs a message at level Debug on the standard logger. -func Debug(args ...interface{}) { +// Debug logs a message at level [DebugLevel] on the standard logger. +func Debug(args ...any) { std.Debug(args...) } -// Print logs a message at level Info on the standard logger. -func Print(args ...interface{}) { +// Print logs a message at level [InfoLevel] on the standard logger. +func Print(args ...any) { std.Print(args...) } -// Info logs a message at level Info on the standard logger. -func Info(args ...interface{}) { +// Info logs a message at level [InfoLevel] on the standard logger. +func Info(args ...any) { std.Info(args...) } -// Warn logs a message at level Warn on the standard logger. -func Warn(args ...interface{}) { +// Warn logs a message at level [WarnLevel] on the standard logger. +func Warn(args ...any) { std.Warn(args...) } -// Warning logs a message at level Warn on the standard logger. -func Warning(args ...interface{}) { +// Warning logs a message at level [WarnLevel] on the standard logger. +func Warning(args ...any) { std.Warning(args...) } -// Error logs a message at level Error on the standard logger. -func Error(args ...interface{}) { +// Error logs a message at level [ErrorLevel] on the standard logger. +func Error(args ...any) { std.Error(args...) } -// Panic logs a message at level Panic on the standard logger. -func Panic(args ...interface{}) { +// Panic logs a message at level [PanicLevel] on the standard logger. +func Panic(args ...any) { std.Panic(args...) } -// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1. -func Fatal(args ...interface{}) { +// Fatal logs a message at level [FatalLevel] on the standard logger, +// then exits the process with status 1. +func Fatal(args ...any) { std.Fatal(args...) } -// TraceFn logs a message from a func at level Trace on the standard logger. +// TraceFn logs a message from a func at level [TraceLevel] on the standard logger. func TraceFn(fn LogFunction) { std.TraceFn(fn) } -// DebugFn logs a message from a func at level Debug on the standard logger. +// DebugFn logs a message from a func at level [DebugLevel] on the standard logger. func DebugFn(fn LogFunction) { std.DebugFn(fn) } -// PrintFn logs a message from a func at level Info on the standard logger. +// PrintFn logs a message from a func at level [InfoLevel] on the standard logger. func PrintFn(fn LogFunction) { std.PrintFn(fn) } -// InfoFn logs a message from a func at level Info on the standard logger. +// InfoFn logs a message from a func at level [InfoLevel] on the standard logger. func InfoFn(fn LogFunction) { std.InfoFn(fn) } -// WarnFn logs a message from a func at level Warn on the standard logger. +// WarnFn logs a message from a func at level [WarnLevel] on the standard logger. func WarnFn(fn LogFunction) { std.WarnFn(fn) } -// WarningFn logs a message from a func at level Warn on the standard logger. +// WarningFn logs a message from a func at level [WarnLevel] on the standard logger. func WarningFn(fn LogFunction) { std.WarningFn(fn) } -// ErrorFn logs a message from a func at level Error on the standard logger. +// ErrorFn logs a message from a func at level [ErrorLevel] on the standard logger. func ErrorFn(fn LogFunction) { std.ErrorFn(fn) } -// PanicFn logs a message from a func at level Panic on the standard logger. +// PanicFn logs a message from a func at level [PanicLevel] on the standard logger. func PanicFn(fn LogFunction) { std.PanicFn(fn) } -// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1. +// FatalFn logs a message from a func at level [FatalLevel] on the standard logger, +// then exits the process with status 1. func FatalFn(fn LogFunction) { std.FatalFn(fn) } -// Tracef logs a message at level Trace on the standard logger. -func Tracef(format string, args ...interface{}) { +// Tracef logs a message at level [TraceLevel] on the standard logger. +func Tracef(format string, args ...any) { std.Tracef(format, args...) } -// Debugf logs a message at level Debug on the standard logger. -func Debugf(format string, args ...interface{}) { +// Debugf logs a message at level [DebugLevel] on the standard logger. +func Debugf(format string, args ...any) { std.Debugf(format, args...) } -// Printf logs a message at level Info on the standard logger. -func Printf(format string, args ...interface{}) { +// Printf logs a message at level [InfoLevel] on the standard logger. +func Printf(format string, args ...any) { std.Printf(format, args...) } -// Infof logs a message at level Info on the standard logger. -func Infof(format string, args ...interface{}) { +// Infof logs a message at level [InfoLevel] on the standard logger. +func Infof(format string, args ...any) { std.Infof(format, args...) } -// Warnf logs a message at level Warn on the standard logger. -func Warnf(format string, args ...interface{}) { +// Warnf logs a message at level [WarnLevel] on the standard logger. +func Warnf(format string, args ...any) { std.Warnf(format, args...) } -// Warningf logs a message at level Warn on the standard logger. -func Warningf(format string, args ...interface{}) { +// Warningf logs a message at level [WarnLevel] on the standard logger. +func Warningf(format string, args ...any) { std.Warningf(format, args...) } -// Errorf logs a message at level Error on the standard logger. -func Errorf(format string, args ...interface{}) { +// Errorf logs a message at level [ErrorLevel] on the standard logger. +func Errorf(format string, args ...any) { std.Errorf(format, args...) } -// Panicf logs a message at level Panic on the standard logger. -func Panicf(format string, args ...interface{}) { +// Panicf logs a message at level [PanicLevel] on the standard logger. +func Panicf(format string, args ...any) { std.Panicf(format, args...) } -// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1. -func Fatalf(format string, args ...interface{}) { +// Fatalf logs a message at level [FatalLevel] on the standard logger, +// then exits the process with status 1. +func Fatalf(format string, args ...any) { std.Fatalf(format, args...) } -// Traceln logs a message at level Trace on the standard logger. -func Traceln(args ...interface{}) { +// Traceln logs a message at level [TraceLevel] on the standard logger. +func Traceln(args ...any) { std.Traceln(args...) } -// Debugln logs a message at level Debug on the standard logger. -func Debugln(args ...interface{}) { +// Debugln logs a message at level [DebugLevel] on the standard logger. +func Debugln(args ...any) { std.Debugln(args...) } -// Println logs a message at level Info on the standard logger. -func Println(args ...interface{}) { +// Println logs a message at level [InfoLevel] on the standard logger. +func Println(args ...any) { std.Println(args...) } -// Infoln logs a message at level Info on the standard logger. -func Infoln(args ...interface{}) { +// Infoln logs a message at level [InfoLevel] on the standard logger. +func Infoln(args ...any) { std.Infoln(args...) } -// Warnln logs a message at level Warn on the standard logger. -func Warnln(args ...interface{}) { +// Warnln logs a message at level [WarnLevel] on the standard logger. +func Warnln(args ...any) { std.Warnln(args...) } -// Warningln logs a message at level Warn on the standard logger. -func Warningln(args ...interface{}) { +// Warningln logs a message at level [WarnLevel] on the standard logger. +func Warningln(args ...any) { std.Warningln(args...) } -// Errorln logs a message at level Error on the standard logger. -func Errorln(args ...interface{}) { +// Errorln logs a message at level [ErrorLevel] on the standard logger. +func Errorln(args ...any) { std.Errorln(args...) } -// Panicln logs a message at level Panic on the standard logger. -func Panicln(args ...interface{}) { +// Panicln logs a message at level [PanicLevel] on the standard logger. +func Panicln(args ...any) { std.Panicln(args...) } -// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1. -func Fatalln(args ...interface{}) { +// Fatalln logs a message at level [FatalLevel] on the standard logger, +// then exits the process with status 1. +func Fatalln(args ...any) { std.Fatalln(args...) } diff --git a/vendor/github.com/sirupsen/logrus/formatter.go b/vendor/github.com/sirupsen/logrus/formatter.go index 40888377..16f2e0e0 100644 --- a/vendor/github.com/sirupsen/logrus/formatter.go +++ b/vendor/github.com/sirupsen/logrus/formatter.go @@ -2,27 +2,40 @@ package logrus import "time" -// Default key names for the default fields const ( + // defaultTimestampFormat is the layout used to format entry timestamps + // when a formatter has not specified a custom TimestampFormat. + // It follows time.RFC3339 and is applied unless timestamps are disabled. defaultTimestampFormat = time.RFC3339 - FieldKeyMsg = "msg" - FieldKeyLevel = "level" - FieldKeyTime = "time" - FieldKeyLogrusError = "logrus_error" - FieldKeyFunc = "func" - FieldKeyFile = "file" + + // defaultFields is the number of commonly included predefined log entry fields + // (msg, level, time). It is used as a capacity hint when constructing + // intermediate collections during formatting (for example, the fixed key list). + // + // It does not include the optional "logrus_error", "func", or "file" fields. + defaultFields = 3 +) + +// Default key names for the default fields +const ( + FieldKeyMsg = "msg" + FieldKeyLevel = "level" + FieldKeyTime = "time" + FieldKeyLogrusError = "logrus_error" + FieldKeyFunc = "func" + FieldKeyFile = "file" ) -// The Formatter interface is used to implement a custom Formatter. It takes an -// `Entry`. It exposes all the fields, including the default ones: +// Formatter is implemented by types that format log entries. It receives an +// [*Entry], which contains: // -// * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. -// * `entry.Data["time"]`. The timestamp. -// * `entry.Data["level"]. The level the entry was logged at. +// - entry.Message: the message passed to logging methods such as [Info], [Warn], [Error] +// - entry.Time: the timestamp +// - entry.Level: the log level // -// Any additional fields added with `WithField` or `WithFields` are also in -// `entry.Data`. Format is expected to return an array of bytes which are then -// logged to `logger.Out`. +// Additional fields added with [WithField] or [WithFields] are available in +// [Entry.Data]. Format should return the formatted log entry as a byte slice, +// which is written to [Logger.Out]. type Formatter interface { Format(*Entry) ([]byte, error) } @@ -30,12 +43,12 @@ type Formatter interface { // This is to not silently overwrite `time`, `msg`, `func` and `level` fields when // dumping it. If this code wasn't there doing: // -// logrus.WithField("level", 1).Info("hello") +// logrus.WithField("level", 1).Info("hello") // // Would just silently drop the user provided level. Instead with this code // it'll logged as: // -// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} +// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} // // It's not exported because it's still using Data in an opinionated way. It's to // avoid code duplication between the two default formatters. diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go index c96dc563..fac7695e 100644 --- a/vendor/github.com/sirupsen/logrus/json_formatter.go +++ b/vendor/github.com/sirupsen/logrus/json_formatter.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "runtime" + "strconv" ) type fieldKey string @@ -20,7 +21,13 @@ func (f FieldMap) resolve(key fieldKey) string { return string(key) } -// JSONFormatter formats logs into parsable json +// JSONFormatter formats logs into parsable JSON. +// +// Fields from [Entry.Data] are included in the JSON object together with the +// standard fields derived from the entry. If a field conflicts with a standard +// field, it is prefixed with "fields.". Standard field names can be customized +// through FieldMap. When DataKey is set, fields from [Entry.Data] are nested +// under that key instead. type JSONFormatter struct { // TimestampFormat sets the format used for marshaling timestamps. // The format to use is the same than for time.Format or time.Parse from the standard @@ -61,7 +68,8 @@ type JSONFormatter struct { // Format renders a single log entry func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { - data := make(Fields, len(entry.Data)+4) + caller := entry.Caller + data := make(Fields, len(entry.Data)+defaultFields) for k, v := range entry.Data { switch v := v.(type) { case error: @@ -73,13 +81,14 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } } - if f.DataKey != "" { - newData := make(Fields, 4) + if f.DataKey != "" && len(entry.Data) > 0 { + newData := make(Fields, defaultFields+1) newData[f.DataKey] = data data = newData } - prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) + hasCaller := caller != nil + prefixFieldClashes(data, f.FieldMap, hasCaller) timestampFormat := f.TimestampFormat if timestampFormat == "" { @@ -94,11 +103,13 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() - if entry.HasCaller() { - funcVal := entry.Caller.Function - fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + if caller != nil { + var funcVal, fileVal string if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + funcVal, fileVal = f.CallerPrettyfier(caller) + } else { + funcVal = caller.Function + fileVal = caller.File + ":" + strconv.FormatInt(int64(caller.Line), 10) } if funcVal != "" { data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal @@ -108,11 +119,9 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } } - var b *bytes.Buffer - if entry.Buffer != nil { - b = entry.Buffer - } else { - b = &bytes.Buffer{} + b := entry.Buffer + if b == nil { + b = new(bytes.Buffer) } encoder := json.NewEncoder(b) diff --git a/vendor/github.com/sirupsen/logrus/level.go b/vendor/github.com/sirupsen/logrus/level.go new file mode 100644 index 00000000..7bd4255d --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/level.go @@ -0,0 +1,101 @@ +package logrus + +import ( + "strings" + "sync" +) + +const ( + ansiReset = "\x1b[0m" // reset attributes + ansiRed = "\x1b[31m" // red + ansiYellow = "\x1b[33m" // yellow + ansiCyan = "\x1b[36m" // cyan + ansiDimCyan = "\x1b[2;36m" // dim cyan + ansiDimWhite = "\x1b[2;37m" // dim white (light gray) +) + +type lvlPrefix struct { + full string + truncated string + padded string +} + +func colorize(level Level, s string) string { + color := ansiCyan + switch level { + case TraceLevel: + color = ansiDimWhite + case DebugLevel: + color = ansiDimCyan + case WarnLevel: + color = ansiYellow + case ErrorLevel, FatalLevel, PanicLevel: + color = ansiRed + case InfoLevel: + color = ansiCyan + } + return color + s + ansiReset +} + +func formatLevel(level Level, disableTrunc, pad bool, maxLen int) string { + upper := strings.ToUpper(level.String()) + + if pad && maxLen > len(upper) { + upper += strings.Repeat(" ", maxLen-len(upper)) + } + + if !pad && !disableTrunc && len(upper) > 4 { + upper = upper[:4] + } + + return colorize(level, upper) +} + +var levelPrefixOnce = sync.OnceValues(func() (map[Level]lvlPrefix, lvlPrefix) { + var maxLevel Level + maxLen := 0 + for _, lvl := range AllLevels { + if lvl > maxLevel { + maxLevel = lvl + } + if l := len(lvl.String()); l > maxLen { + maxLen = l + } + } + + prefix := make(map[Level]lvlPrefix, len(AllLevels)) + for _, lvl := range AllLevels { + prefix[lvl] = lvlPrefix{ + full: formatLevel(lvl, true, false, maxLen), + truncated: formatLevel(lvl, false, false, maxLen), + padded: formatLevel(lvl, true, true, maxLen), + } + } + + unknownLevel := maxLevel + 1 + unknown := lvlPrefix{ + full: formatLevel(unknownLevel, true, false, maxLen), + truncated: formatLevel(unknownLevel, false, false, maxLen), + padded: formatLevel(unknownLevel, true, true, maxLen), + } + + return prefix, unknown +}) + +func levelPrefix(level Level, disableTrunc, pad bool) string { + prefix, unknown := levelPrefixOnce() + + p, ok := prefix[level] + if !ok { + p = unknown + } + + switch { + case pad: + return p.padded + case !disableTrunc: + return p.truncated + default: + return p.full + } +} diff --git a/vendor/github.com/sirupsen/logrus/logger.go b/vendor/github.com/sirupsen/logrus/logger.go index f5b8c439..17a46e6a 100644 --- a/vendor/github.com/sirupsen/logrus/logger.go +++ b/vendor/github.com/sirupsen/logrus/logger.go @@ -12,17 +12,19 @@ import ( // LogFunction For big messages, it can be more efficient to pass a function // and only call it if the log level is actually enables rather than // generating the log message and then checking if the level is enabled -type LogFunction func() []interface{} +type LogFunction func() []any type Logger struct { // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a // file, or leave it default which is `os.Stderr`. You can also set this to // something more adventurous, such as logging to Kafka. Out io.Writer + // Hooks for the logger instance. These allow firing events based on logging // levels and log entries. For example, to send errors to an error tracking // service, log to StatsD or dump the core on fatal errors. Hooks LevelHooks + // All log entries pass through the formatter before logged to Out. The // included formatters are `TextFormatter` and `JSONFormatter` for which // TextFormatter is the default. In development (when a TTY is attached) it @@ -38,37 +40,44 @@ type Logger struct { // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be // logged. Level Level + // Used to sync writing to the log. Locking is enabled by Default - mu MutexWrap + mu mutexWrap + // Reusable empty entry entryPool sync.Pool + // Function to exit the application, defaults to `os.Exit()` - ExitFunc exitFunc + ExitFunc func(int) + // The buffer pool used to format the log. If it is nil, the default global // buffer pool will be used. BufferPool BufferPool } -type exitFunc func(int) +// MutexWrap is the mutex implementation used by [Logger]. +// +// Deprecated: MutexWrap is an implementation detail of Logger and should not be used directly. +type MutexWrap = mutexWrap -type MutexWrap struct { +type mutexWrap struct { lock sync.Mutex disabled bool } -func (mw *MutexWrap) Lock() { +func (mw *mutexWrap) Lock() { if !mw.disabled { mw.lock.Lock() } } -func (mw *MutexWrap) Unlock() { +func (mw *mutexWrap) Unlock() { if !mw.disabled { mw.lock.Unlock() } } -func (mw *MutexWrap) Disable() { +func (mw *mutexWrap) Disable() { mw.disabled = true } @@ -104,7 +113,7 @@ func (logger *Logger) newEntry() *Entry { } func (logger *Logger) releaseEntry(entry *Entry) { - entry.Data = map[string]interface{}{} + entry.Data = map[string]any{} logger.entryPool.Put(entry) } @@ -112,7 +121,7 @@ func (logger *Logger) releaseEntry(entry *Entry) { // Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to // this new returned entry. // If you want multiple fields, use `WithFields`. -func (logger *Logger) WithField(key string, value interface{}) *Entry { +func (logger *Logger) WithField(key string, value any) *Entry { entry := logger.newEntry() defer logger.releaseEntry(entry) return entry.WithField(key, value) @@ -148,7 +157,12 @@ func (logger *Logger) WithTime(t time.Time) *Entry { return entry.WithTime(t) } -func (logger *Logger) Logf(level Level, format string, args ...interface{}) { +// Logf logs a formatted message at the specified level. +// +// Using Logf with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Logf treats the level as logging severity only; +// use [Logger.Panicf] or [Logger.Fatalf] when those side effects are desired. +func (logger *Logger) Logf(level Level, format string, args ...any) { if logger.IsLevelEnabled(level) { entry := logger.newEntry() entry.Logf(level, format, args...) @@ -156,49 +170,55 @@ func (logger *Logger) Logf(level Level, format string, args ...interface{}) { } } -func (logger *Logger) Tracef(format string, args ...interface{}) { +func (logger *Logger) Tracef(format string, args ...any) { logger.Logf(TraceLevel, format, args...) } -func (logger *Logger) Debugf(format string, args ...interface{}) { +func (logger *Logger) Debugf(format string, args ...any) { logger.Logf(DebugLevel, format, args...) } -func (logger *Logger) Infof(format string, args ...interface{}) { +func (logger *Logger) Infof(format string, args ...any) { logger.Logf(InfoLevel, format, args...) } -func (logger *Logger) Printf(format string, args ...interface{}) { +func (logger *Logger) Printf(format string, args ...any) { entry := logger.newEntry() entry.Printf(format, args...) logger.releaseEntry(entry) } -func (logger *Logger) Warnf(format string, args ...interface{}) { +func (logger *Logger) Warnf(format string, args ...any) { logger.Logf(WarnLevel, format, args...) } -func (logger *Logger) Warningf(format string, args ...interface{}) { +func (logger *Logger) Warningf(format string, args ...any) { logger.Warnf(format, args...) } -func (logger *Logger) Errorf(format string, args ...interface{}) { +func (logger *Logger) Errorf(format string, args ...any) { logger.Logf(ErrorLevel, format, args...) } -func (logger *Logger) Fatalf(format string, args ...interface{}) { +func (logger *Logger) Fatalf(format string, args ...any) { logger.Logf(FatalLevel, format, args...) logger.Exit(1) } -func (logger *Logger) Panicf(format string, args ...interface{}) { - logger.Logf(PanicLevel, format, args...) +func (logger *Logger) Panicf(format string, args ...any) { + if logger.IsLevelEnabled(PanicLevel) { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + entry.Panicf(format, args...) + } } -// Log will log a message at the level given as parameter. -// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit. -// For this behaviour Logger.Panic or Logger.Fatal should be used instead. -func (logger *Logger) Log(level Level, args ...interface{}) { +// Log logs a message at the specified level. +// +// Using Log with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Log treats the level as logging severity only; +// use [Logger.Panic] or [Logger.Fatal] when those side effects are desired. +func (logger *Logger) Log(level Level, args ...any) { if logger.IsLevelEnabled(level) { entry := logger.newEntry() entry.Log(level, args...) @@ -206,6 +226,11 @@ func (logger *Logger) Log(level Level, args ...interface{}) { } } +// LogFn logs a message returned by fn at the specified level. +// +// Using LogFn with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. LogFn treats the level as logging severity only; +// use [Logger.PanicFn] or [Logger.FatalFn] when those side effects are desired. func (logger *Logger) LogFn(level Level, fn LogFunction) { if logger.IsLevelEnabled(level) { entry := logger.newEntry() @@ -214,43 +239,47 @@ func (logger *Logger) LogFn(level Level, fn LogFunction) { } } -func (logger *Logger) Trace(args ...interface{}) { +func (logger *Logger) Trace(args ...any) { logger.Log(TraceLevel, args...) } -func (logger *Logger) Debug(args ...interface{}) { +func (logger *Logger) Debug(args ...any) { logger.Log(DebugLevel, args...) } -func (logger *Logger) Info(args ...interface{}) { +func (logger *Logger) Info(args ...any) { logger.Log(InfoLevel, args...) } -func (logger *Logger) Print(args ...interface{}) { +func (logger *Logger) Print(args ...any) { entry := logger.newEntry() entry.Print(args...) logger.releaseEntry(entry) } -func (logger *Logger) Warn(args ...interface{}) { +func (logger *Logger) Warn(args ...any) { logger.Log(WarnLevel, args...) } -func (logger *Logger) Warning(args ...interface{}) { +func (logger *Logger) Warning(args ...any) { logger.Warn(args...) } -func (logger *Logger) Error(args ...interface{}) { +func (logger *Logger) Error(args ...any) { logger.Log(ErrorLevel, args...) } -func (logger *Logger) Fatal(args ...interface{}) { +func (logger *Logger) Fatal(args ...any) { logger.Log(FatalLevel, args...) logger.Exit(1) } -func (logger *Logger) Panic(args ...interface{}) { - logger.Log(PanicLevel, args...) +func (logger *Logger) Panic(args ...any) { + if logger.IsLevelEnabled(PanicLevel) { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + entry.Panic(args...) + } } func (logger *Logger) TraceFn(fn LogFunction) { @@ -289,10 +318,19 @@ func (logger *Logger) FatalFn(fn LogFunction) { } func (logger *Logger) PanicFn(fn LogFunction) { - logger.LogFn(PanicLevel, fn) + if logger.IsLevelEnabled(PanicLevel) { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + entry.Panic(fn()...) + } } -func (logger *Logger) Logln(level Level, args ...interface{}) { +// Logln logs a message at the specified level with Println-style spacing. +// +// Using Logln with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Logln treats the level as logging severity only; +// use [Logger.Panicln] or [Logger.Fatalln] when those side effects are desired. +func (logger *Logger) Logln(level Level, args ...any) { if logger.IsLevelEnabled(level) { entry := logger.newEntry() entry.Logln(level, args...) @@ -300,43 +338,47 @@ func (logger *Logger) Logln(level Level, args ...interface{}) { } } -func (logger *Logger) Traceln(args ...interface{}) { +func (logger *Logger) Traceln(args ...any) { logger.Logln(TraceLevel, args...) } -func (logger *Logger) Debugln(args ...interface{}) { +func (logger *Logger) Debugln(args ...any) { logger.Logln(DebugLevel, args...) } -func (logger *Logger) Infoln(args ...interface{}) { +func (logger *Logger) Infoln(args ...any) { logger.Logln(InfoLevel, args...) } -func (logger *Logger) Println(args ...interface{}) { +func (logger *Logger) Println(args ...any) { entry := logger.newEntry() entry.Println(args...) logger.releaseEntry(entry) } -func (logger *Logger) Warnln(args ...interface{}) { +func (logger *Logger) Warnln(args ...any) { logger.Logln(WarnLevel, args...) } -func (logger *Logger) Warningln(args ...interface{}) { +func (logger *Logger) Warningln(args ...any) { logger.Warnln(args...) } -func (logger *Logger) Errorln(args ...interface{}) { +func (logger *Logger) Errorln(args ...any) { logger.Logln(ErrorLevel, args...) } -func (logger *Logger) Fatalln(args ...interface{}) { +func (logger *Logger) Fatalln(args ...any) { logger.Logln(FatalLevel, args...) logger.Exit(1) } -func (logger *Logger) Panicln(args ...interface{}) { - logger.Logln(PanicLevel, args...) +func (logger *Logger) Panicln(args ...any) { + if logger.IsLevelEnabled(PanicLevel) { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + entry.Panicln(args...) + } } func (logger *Logger) Exit(code int) { @@ -375,7 +417,22 @@ func (logger *Logger) AddHook(hook Hook) { logger.Hooks.Add(hook) } -// IsLevelEnabled checks if the log level of the logger is greater than the level param +// hooksForLevel returns a snapshot of the hooks registered for the given level. +// The returned slice is a shallow copy and may be used without holding logger.mu. +func (logger *Logger) hooksForLevel(level Level) []Hook { + logger.mu.Lock() + hooks := logger.Hooks[level] + if len(hooks) == 0 { + logger.mu.Unlock() + return nil + } + out := make([]Hook, len(hooks)) + copy(out, hooks) + logger.mu.Unlock() + return out +} + +// IsLevelEnabled checks if logging for the given level is enabled. func (logger *Logger) IsLevelEnabled(level Level) bool { return logger.level() >= level } @@ -394,6 +451,7 @@ func (logger *Logger) SetOutput(output io.Writer) { logger.Out = output } +// SetReportCaller sets whether the caller stack frame must be logged. func (logger *Logger) SetReportCaller(reportCaller bool) { logger.mu.Lock() defer logger.mu.Unlock() @@ -403,9 +461,9 @@ func (logger *Logger) SetReportCaller(reportCaller bool) { // ReplaceHooks replaces the logger hooks and returns the old ones func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks { logger.mu.Lock() + defer logger.mu.Unlock() oldHooks := logger.Hooks logger.Hooks = hooks - logger.mu.Unlock() return oldHooks } diff --git a/vendor/github.com/sirupsen/logrus/logrus.go b/vendor/github.com/sirupsen/logrus/logrus.go index 37fc4fef..d52b4ba7 100644 --- a/vendor/github.com/sirupsen/logrus/logrus.go +++ b/vendor/github.com/sirupsen/logrus/logrus.go @@ -1,13 +1,13 @@ package logrus import ( + "bytes" "fmt" "log" - "strings" ) // Fields type, used to pass to [WithFields]. -type Fields map[string]interface{} +type Fields map[string]any // Level type // @@ -16,39 +16,56 @@ type Level uint32 // Convert the Level to a string. E.g. [PanicLevel] becomes "panic". func (level Level) String() string { - if b, err := level.MarshalText(); err == nil { - return string(b) - } else { + switch level { + case TraceLevel: + return "trace" + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarnLevel: + return "warning" + case ErrorLevel: + return "error" + case FatalLevel: + return "fatal" + case PanicLevel: + return "panic" + default: return "unknown" } } // ParseLevel takes a string level and returns the Logrus log level constant. func ParseLevel(lvl string) (Level, error) { - switch strings.ToLower(lvl) { - case "panic": + return parseLevel([]byte(lvl)) +} + +func parseLevel(b []byte) (Level, error) { + switch { + case bytes.EqualFold(b, []byte("panic")): return PanicLevel, nil - case "fatal": + case bytes.EqualFold(b, []byte("fatal")): return FatalLevel, nil - case "error": + case bytes.EqualFold(b, []byte("error")): return ErrorLevel, nil - case "warn", "warning": + case bytes.EqualFold(b, []byte("warn")), + bytes.EqualFold(b, []byte("warning")): return WarnLevel, nil - case "info": + case bytes.EqualFold(b, []byte("info")): return InfoLevel, nil - case "debug": + case bytes.EqualFold(b, []byte("debug")): return DebugLevel, nil - case "trace": + case bytes.EqualFold(b, []byte("trace")): return TraceLevel, nil + default: + return 0, fmt.Errorf("not a valid logrus Level: %q", b) } - - var l Level - return l, fmt.Errorf("not a valid logrus Level: %q", lvl) } // UnmarshalText implements encoding.TextUnmarshaler. func (level *Level) UnmarshalText(text []byte) error { - l, err := ParseLevel(string(text)) + l, err := parseLevel(text) if err != nil { return err } @@ -60,23 +77,11 @@ func (level *Level) UnmarshalText(text []byte) error { func (level Level) MarshalText() ([]byte, error) { switch level { - case TraceLevel: - return []byte("trace"), nil - case DebugLevel: - return []byte("debug"), nil - case InfoLevel: - return []byte("info"), nil - case WarnLevel: - return []byte("warning"), nil - case ErrorLevel: - return []byte("error"), nil - case FatalLevel: - return []byte("fatal"), nil - case PanicLevel: - return []byte("panic"), nil + case TraceLevel, DebugLevel, InfoLevel, WarnLevel, ErrorLevel, FatalLevel, PanicLevel: + return []byte(level.String()), nil + default: + return nil, fmt.Errorf("not a valid logrus level %d", level) } - - return nil, fmt.Errorf("not a valid logrus level %d", level) } // AllLevels exposing all logging levels. @@ -91,7 +96,7 @@ var AllLevels = []Level{ } // These are the different logging levels. You can set the logging level to log -// on your instance of logger, obtained with `logrus.New()`. +// on your instance of logger, obtained with [logrus.New]. const ( // PanicLevel level, highest level of severity. Logs and then calls panic with the // message passed to Debug, Info, ... @@ -113,78 +118,110 @@ const ( TraceLevel ) -// Won't compile if StdLogger can't be realized by a log.Logger +// Compile-time interface assertions. var ( - _ StdLogger = &log.Logger{} - _ StdLogger = &Entry{} - _ StdLogger = &Logger{} + _ StdLogger = (*log.Logger)(nil) + _ StdLogger = (*Entry)(nil) + _ StdLogger = (*Logger)(nil) + + _ FieldLogger = (*Logger)(nil) + _ FieldLogger = (*Entry)(nil) + _ FieldLogger = Ext1FieldLogger(nil) + + _ DebugLogger = (*Logger)(nil) + _ InfoLogger = (*Logger)(nil) + _ WarnLogger = (*Logger)(nil) + _ ErrorLogger = (*Logger)(nil) + _ TraceLogger = (*Logger)(nil) + + _ DebugLogger = (*Entry)(nil) + _ InfoLogger = (*Entry)(nil) + _ WarnLogger = (*Entry)(nil) + _ ErrorLogger = (*Entry)(nil) + _ TraceLogger = (*Entry)(nil) + + _ Ext1FieldLogger = (*Logger)(nil) + _ Ext1FieldLogger = (*Entry)(nil) ) // StdLogger is what your logrus-enabled library should take, that way // it'll accept a stdlib logger ([log.Logger]) and a logrus logger. // There's no standard interface, so this is the closest we get, unfortunately. type StdLogger interface { - Print(...interface{}) - Printf(string, ...interface{}) - Println(...interface{}) + Print(args ...any) + Printf(format string, args ...any) + Println(args ...any) - Fatal(...interface{}) - Fatalf(string, ...interface{}) - Fatalln(...interface{}) + Fatal(args ...any) + Fatalf(format string, args ...any) + Fatalln(args ...any) - Panic(...interface{}) - Panicf(string, ...interface{}) - Panicln(...interface{}) + Panic(args ...any) + Panicf(format string, args ...any) + Panicln(args ...any) } // FieldLogger extends the [StdLogger] interface, generalizing // the [Entry] and [Logger] types. type FieldLogger interface { - WithField(key string, value interface{}) *Entry + WithField(key string, value any) *Entry WithFields(fields Fields) *Entry WithError(err error) *Entry - Debugf(format string, args ...interface{}) - Infof(format string, args ...interface{}) - Printf(format string, args ...interface{}) - Warnf(format string, args ...interface{}) - Warningf(format string, args ...interface{}) - Errorf(format string, args ...interface{}) - Fatalf(format string, args ...interface{}) - Panicf(format string, args ...interface{}) - - Debug(args ...interface{}) - Info(args ...interface{}) - Print(args ...interface{}) - Warn(args ...interface{}) - Warning(args ...interface{}) - Error(args ...interface{}) - Fatal(args ...interface{}) - Panic(args ...interface{}) - - Debugln(args ...interface{}) - Infoln(args ...interface{}) - Println(args ...interface{}) - Warnln(args ...interface{}) - Warningln(args ...interface{}) - Errorln(args ...interface{}) - Fatalln(args ...interface{}) - Panicln(args ...interface{}) - - // IsDebugEnabled() bool - // IsInfoEnabled() bool - // IsWarnEnabled() bool - // IsErrorEnabled() bool - // IsFatalEnabled() bool - // IsPanicEnabled() bool + StdLogger + DebugLogger + InfoLogger + WarnLogger + ErrorLogger + + // Legacy warning aliases. These are kept on FieldLogger for backwards + // compatibility, but are intentionally omitted from [WarnLogger]. + + Warning(args ...any) + Warningf(format string, args ...any) + Warningln(args ...any) +} + +// DebugLogger provides convenience functions to log messages at level [DebugLevel]. +type DebugLogger interface { + Debug(args ...any) + Debugf(format string, args ...any) + Debugln(args ...any) +} + +// InfoLogger provides convenience functions to log messages at level [InfoLevel]. +type InfoLogger interface { + Info(args ...any) + Infof(format string, args ...any) + Infoln(args ...any) +} + +// WarnLogger provides convenience functions to log messages at level [WarnLevel]. +type WarnLogger interface { + Warn(args ...any) + Warnf(format string, args ...any) + Warnln(args ...any) +} + +// ErrorLogger provides convenience functions to log messages at level [ErrorLevel]. +type ErrorLogger interface { + Error(args ...any) + Errorf(format string, args ...any) + Errorln(args ...any) +} + +// TraceLogger provides convenience functions to log messages at level [TraceLevel]. +type TraceLogger interface { + Trace(args ...any) + Tracef(format string, args ...any) + Traceln(args ...any) } -// Ext1FieldLogger (the first extension to [FieldLogger]) is superfluous, it is -// here for consistency. Do not use. Use [FieldLogger], [Logger] or [Entry] -// instead. +// Ext1FieldLogger is FieldLogger extended with Trace-level methods. +// +// New code should prefer the smallest applicable interface, such as +// [FieldLogger] or [TraceLogger], or use [Logger] or [Entry] directly. type Ext1FieldLogger interface { FieldLogger - Tracef(format string, args ...interface{}) - Trace(args ...interface{}) - Traceln(args ...interface{}) + TraceLogger } diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go index 2403de98..1c6202b8 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go @@ -1,11 +1,7 @@ -// +build appengine +//go:build appengine package logrus -import ( - "io" -) - -func checkIfTerminal(w io.Writer) bool { +func checkIfTerminal(_ any) bool { return true } diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go index 69956b42..ff9531ac 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go @@ -1,5 +1,4 @@ -// +build darwin dragonfly freebsd netbsd openbsd hurd -// +build !js +//go:build (darwin || dragonfly || freebsd || netbsd || openbsd || hurd) && !tinygo package logrus diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_js.go b/vendor/github.com/sirupsen/logrus/terminal_check_js.go deleted file mode 100644 index ebdae3ec..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_js.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build js - -package logrus - -func isTerminal(fd int) bool { - return false -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go index 97af92c6..17ae9f04 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go @@ -1,11 +1,7 @@ -// +build js nacl plan9 +//go:build js || nacl || plan9 || wasi || wasip1 || tinygo package logrus -import ( - "io" -) - -func checkIfTerminal(w io.Writer) bool { +func checkIfTerminal(_ any) bool { return false } diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go index 3293fb3c..780a42a5 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go @@ -1,4 +1,4 @@ -// +build !appengine,!js,!windows,!nacl,!plan9 +//go:build !appengine && !js && !windows && !nacl && !plan9 && !wasi && !wasip1 && !tinygo package logrus @@ -10,7 +10,11 @@ import ( func checkIfTerminal(w io.Writer) bool { switch v := w.(type) { case *os.File: - return isTerminal(int(v.Fd())) + fd := v.Fd() + if fd > uintptr(^uint(0)>>1) { + return false + } + return isTerminal(int(fd)) default: return false } diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go index f6710b3b..8d9b26fc 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go @@ -1,3 +1,5 @@ +//go:build solaris && !tinygo + package logrus import ( diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go index c9aed267..b161506d 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go @@ -1,7 +1,4 @@ -//go:build (linux || aix || zos) && !js && !wasi -// +build linux aix zos -// +build !js -// +build !wasi +//go:build (linux || aix || zos) && !tinygo package logrus diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_wasi.go b/vendor/github.com/sirupsen/logrus/terminal_check_wasi.go deleted file mode 100644 index 2822b212..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_wasi.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build wasi -// +build wasi - -package logrus - -func isTerminal(fd int) bool { - return false -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go b/vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go deleted file mode 100644 index 108a6be1..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build wasip1 -// +build wasip1 - -package logrus - -func isTerminal(fd int) bool { - return false -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go index 2879eb50..5fd3c431 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go @@ -1,4 +1,4 @@ -// +build !appengine,!js,windows +//go:build windows && !appengine package logrus diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go index 6dfeb18b..82c1f3da 100644 --- a/vendor/github.com/sirupsen/logrus/text_formatter.go +++ b/vendor/github.com/sirupsen/logrus/text_formatter.go @@ -3,30 +3,32 @@ package logrus import ( "bytes" "fmt" + "maps" "os" + "reflect" "runtime" - "sort" + "slices" "strconv" "strings" "sync" "time" - "unicode/utf8" ) -const ( - red = 31 - yellow = 33 - blue = 36 - gray = 37 -) - -var baseTimestamp time.Time - -func init() { - baseTimestamp = time.Now() -} - -// TextFormatter formats logs into text +var baseTimestamp = time.Now() + +// TextFormatter formats logs into text. +// +// Output is logfmt-like: key=value pairs separated by spaces. Fields from +// [Entry.Data] are included together with the standard fields derived from the +// entry. If a field conflicts with a standard field, it is prefixed with +// "fields.". Standard field names can be customized through FieldMap. +// +// Field keys are written as-is (unquoted and unescaped) in the plain +// (non-colored) format; only field values may be quoted depending on +// DisableQuote, ForceQuote, QuoteEmptyFields, and the value content. +// +// When colors are enabled, ANSI escape sequences may be added for presentation. +// For fully escaped structured output (including safe keys), use JSONFormatter. type TextFormatter struct { // Set to true to bypass checking for a TTY before outputting colors. ForceColors bool @@ -64,7 +66,7 @@ type TextFormatter struct { // be desired. DisableSorting bool - // The keys sorting function, when uninitialized it uses sort.Strings. + // The keys sorting function, when uninitialized it uses slices.Sort. SortingFunc func([]string) // Disables the truncation of the level text to 4 characters. @@ -77,16 +79,22 @@ type TextFormatter struct { // QuoteEmptyFields will wrap empty fields in quotes if true QuoteEmptyFields bool - // Whether the logger's out is to a terminal - isTerminal bool + // Whether the logger's out is to a terminal. Don't use this field + // directly; use TextFormatter.isTerminal instead. + terminal bool // FieldMap allows users to customize the names of keys for default fields. + // Mapped keys are written as-is, so they should be safe for plain-text output. + // // As an example: + // // formatter := &TextFormatter{ - // FieldMap: FieldMap{ - // FieldKeyTime: "@timestamp", - // FieldKeyLevel: "@level", - // FieldKeyMsg: "@message"}} + // FieldMap: FieldMap{ + // FieldKeyTime: "@timestamp", + // FieldKeyLevel: "@level", + // FieldKeyMsg: "@message", + // }, + // } FieldMap FieldMap // CallerPrettyfier can be set by the user to modify the content @@ -96,54 +104,78 @@ type TextFormatter struct { CallerPrettyfier func(*runtime.Frame) (function string, file string) terminalInitOnce sync.Once - - // The max length of the level text, generated dynamically on init - levelTextMaxLength int } -func (f *TextFormatter) init(entry *Entry) { - if entry.Logger != nil { - f.isTerminal = checkIfTerminal(entry.Logger.Out) - } - // Get the max length of the level text - for _, level := range AllLevels { - levelTextLength := utf8.RuneCount([]byte(level.String())) - if levelTextLength > f.levelTextMaxLength { - f.levelTextMaxLength = levelTextLength - } +func (f *TextFormatter) isTerminal(entry *Entry) bool { + if entry == nil || entry.Logger == nil { + // Don't run the terminalInitOnce without a logger, otherwise we'd + // cache the default (false) forever even if a logger is attached + // later. + return false } -} -func (f *TextFormatter) isColored() bool { - isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows")) + f.terminalInitOnce.Do(func() { + entry.Logger.mu.Lock() + out := entry.Logger.Out + entry.Logger.mu.Unlock() - if f.EnvironmentOverrideColors { - switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); { - case ok && force != "0": - isColored = true - case ok && force == "0", os.Getenv("CLICOLOR") == "0": - isColored = false - } + f.terminal = checkIfTerminal(out) + }) + + return f.terminal +} + +func (f *TextFormatter) isColored(isTerminal bool) bool { + if f.DisableColors { + return false } - return isColored && !f.DisableColors + colored := f.ForceColors || isTerminal + if !f.EnvironmentOverrideColors { + return colored + } + if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok { + return force != "0" + } + if os.Getenv("CLICOLOR") == "0" { + return false + } + return colored } // Format renders a single log entry func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { - data := make(Fields) - for k, v := range entry.Data { - data[k] = v - } - prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) + data := make(Fields, len(entry.Data)) + maps.Copy(data, entry.Data) + isColored := f.isColored(f.isTerminal(entry)) + + caller := entry.Caller + hasCaller := caller != nil + prefixFieldClashes(data, f.FieldMap, hasCaller) keys := make([]string, 0, len(data)) for k := range data { keys = append(keys, k) } - var funcVal, fileVal string + b := entry.Buffer + if b == nil { + b = new(bytes.Buffer) + } + + if isColored { + f.printColored(b, entry, keys, data) + } else { + f.printPlain(b, entry, keys, data) + } + + return b.Bytes(), nil +} + +func (f *TextFormatter) printPlain(b *bytes.Buffer, entry *Entry, keys []string, data Fields) { + caller := entry.Caller + hasCaller := caller != nil - fixedKeys := make([]string, 0, 4+len(data)) + fixedKeys := make([]string, 0, len(keys)+defaultFields) if !f.DisableTimestamp { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) } @@ -154,12 +186,14 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { if entry.err != "" { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError)) } - if entry.HasCaller() { + + var funcVal, fileVal string + if caller != nil { if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + funcVal, fileVal = f.CallerPrettyfier(caller) } else { - funcVal = entry.Caller.Function - fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + funcVal = caller.Function + fileVal = caller.File + ":" + strconv.FormatInt(int64(caller.Line), 10) } if funcVal != "" { @@ -172,169 +206,278 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { if !f.DisableSorting { if f.SortingFunc == nil { - sort.Strings(keys) + // Default sorting does not sort the "fixed keys"; + // see https://github.com/sirupsen/logrus/commit/73bc94e60c753099e8bae902f81fbd6e7dd95f26 + slices.Sort(keys) fixedKeys = append(fixedKeys, keys...) } else { - if !f.isColored() { - fixedKeys = append(fixedKeys, keys...) - f.SortingFunc(fixedKeys) - } else { - f.SortingFunc(keys) - } + fixedKeys = append(fixedKeys, keys...) + f.SortingFunc(fixedKeys) } } else { fixedKeys = append(fixedKeys, keys...) } - var b *bytes.Buffer - if entry.Buffer != nil { - b = entry.Buffer - } else { - b = &bytes.Buffer{} - } - - f.terminalInitOnce.Do(func() { f.init(entry) }) - - timestampFormat := f.TimestampFormat - if timestampFormat == "" { - timestampFormat = defaultTimestampFormat - } - if f.isColored() { - f.printColored(b, entry, keys, data, timestampFormat) - } else { - - for _, key := range fixedKeys { - var value interface{} - switch { - case key == f.FieldMap.resolve(FieldKeyTime): - value = entry.Time.Format(timestampFormat) - case key == f.FieldMap.resolve(FieldKeyLevel): - value = entry.Level.String() - case key == f.FieldMap.resolve(FieldKeyMsg): - value = entry.Message - case key == f.FieldMap.resolve(FieldKeyLogrusError): - value = entry.err - case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller(): - value = funcVal - case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller(): - value = fileVal - default: - value = data[key] + for _, key := range fixedKeys { + var value any + switch { + case key == f.FieldMap.resolve(FieldKeyTime): + if f.TimestampFormat == "" { + value = entry.Time.Format(defaultTimestampFormat) + } else { + value = entry.Time.Format(f.TimestampFormat) } - f.appendKeyValue(b, key, value) + case key == f.FieldMap.resolve(FieldKeyLevel): + value = entry.Level.String() + case key == f.FieldMap.resolve(FieldKeyMsg): + value = entry.Message + case key == f.FieldMap.resolve(FieldKeyLogrusError): + value = entry.err + case key == f.FieldMap.resolve(FieldKeyFunc) && hasCaller: + value = funcVal + case key == f.FieldMap.resolve(FieldKeyFile) && hasCaller: + value = fileVal + default: + value = data[key] } + f.appendKeyValue(b, key, value) } b.WriteByte('\n') - return b.Bytes(), nil } -func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) { - var levelColor int - switch entry.Level { - case DebugLevel, TraceLevel: - levelColor = gray - case WarnLevel: - levelColor = yellow - case ErrorLevel, FatalLevel, PanicLevel: - levelColor = red - case InfoLevel: - levelColor = blue - default: - levelColor = blue - } - - levelText := strings.ToUpper(entry.Level.String()) - if !f.DisableLevelTruncation && !f.PadLevelText { - levelText = levelText[0:4] - } - if f.PadLevelText { - // Generates the format string used in the next line, for example "%-6s" or "%-7s". - // Based on the max level text length. - formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s" - // Formats the level text by appending spaces up to the max length, for example: - // - "INFO " - // - "WARNING" - levelText = fmt.Sprintf(formatString, levelText) - } - +func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields) { // Remove a single newline if it already exists in the message to keep // the behavior of logrus text_formatter the same as the stdlib log package entry.Message = strings.TrimSuffix(entry.Message, "\n") - caller := "" - if entry.HasCaller() { - funcVal := fmt.Sprintf("%s()", entry.Caller.Function) - fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) - + var callerText string + if caller := entry.Caller; caller != nil { + var funcVal, fileVal string if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + funcVal, fileVal = f.CallerPrettyfier(caller) + } else { + if caller.Function != "" { + funcVal = caller.Function + "()" + } + fileVal = caller.File + ":" + strconv.FormatInt(int64(caller.Line), 10) } if fileVal == "" { - caller = funcVal + callerText = funcVal } else if funcVal == "" { - caller = fileVal + callerText = fileVal } else { - caller = fileVal + " " + funcVal + callerText = fileVal + " " + funcVal } } + levelText := levelPrefix(entry.Level, f.DisableLevelTruncation, f.PadLevelText) switch { case f.DisableTimestamp: - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message) + _, _ = fmt.Fprintf(b, "%s%s %-44s ", levelText, callerText, entry.Message) case !f.FullTimestamp: - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message) + _, _ = fmt.Fprintf(b, "%s[%04d]%s %-44s ", levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), callerText, entry.Message) default: - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message) + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = defaultTimestampFormat + } + _, _ = fmt.Fprintf(b, "%s[%s]%s %-44s ", levelText, entry.Time.Format(timestampFormat), callerText, entry.Message) + } + + if !f.DisableSorting { + if f.SortingFunc == nil { + slices.Sort(keys) + } else { + f.SortingFunc(keys) + } } + + // Keys use the same color as the level-prefix. for _, k := range keys { - v := data[k] - fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) - f.appendValue(b, v) + b.WriteByte(' ') + b.WriteString(colorize(entry.Level, k)) + b.WriteByte('=') + f.appendValue(b, data[k]) } + + b.WriteByte('\n') } -func (f *TextFormatter) needsQuoting(text string) bool { - if f.ForceQuote { - return true +// appendKeyValue writes key=value. Keys are written verbatim (unquoted/unescaped); +// values are subject to quoting/escaping. +func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value any) { + if b.Len() > 0 { + b.WriteByte(' ') } - if f.QuoteEmptyFields && len(text) == 0 { - return true + b.WriteString(key) + b.WriteByte('=') + f.appendValue(b, value) +} + +func (f *TextFormatter) appendValue(b *bytes.Buffer, value any) { + // Fast paths. + switch v := value.(type) { + case string: + f.appendString(b, v) + return + case []byte: + f.appendBytes(b, v) + return + case bool: + var raw [8]byte + f.appendBytes(b, strconv.AppendBool(raw[:0], v)) + return + case error: + f.appendError(b, v) + return + case fmt.Stringer: + f.appendStringer(b, v) + return } - if f.DisableQuote { - return false + + // Handle common primitives. + var raw [64]byte + var num []byte + + switch v := value.(type) { + case int: + num = strconv.AppendInt(raw[:0], int64(v), 10) + case int8: + num = strconv.AppendInt(raw[:0], int64(v), 10) + case int16: + num = strconv.AppendInt(raw[:0], int64(v), 10) + case int32: + num = strconv.AppendInt(raw[:0], int64(v), 10) + case int64: + num = strconv.AppendInt(raw[:0], v, 10) + + case uint: + num = strconv.AppendUint(raw[:0], uint64(v), 10) + case uint8: + num = strconv.AppendUint(raw[:0], uint64(v), 10) + case uint16: + num = strconv.AppendUint(raw[:0], uint64(v), 10) + case uint32: + num = strconv.AppendUint(raw[:0], uint64(v), 10) + case uint64: + num = strconv.AppendUint(raw[:0], v, 10) + case uintptr: + num = strconv.AppendUint(raw[:0], uint64(v), 10) + + case float32: + num = strconv.AppendFloat(raw[:0], float64(v), 'g', -1, 32) + case float64: + num = strconv.AppendFloat(raw[:0], v, 'g', -1, 64) + + default: + f.appendString(b, fmt.Sprint(value)) + return + } + + f.appendNumeric(b, num) +} + +func (f *TextFormatter) appendString(b *bytes.Buffer, s string) { + quote := f.ForceQuote || (f.QuoteEmptyFields && len(s) == 0) || (!f.DisableQuote && needsQuoting(s)) + if !quote { + b.WriteString(s) + return + } + if len(s) == 0 { + b.WriteString(`""`) + return + } + + var tmp [128]byte + b.Write(strconv.AppendQuote(tmp[:0], s)) +} + +func (f *TextFormatter) appendBytes(b *bytes.Buffer, bs []byte) { + quote := f.ForceQuote || (f.QuoteEmptyFields && len(bs) == 0) || (!f.DisableQuote && needsQuotingBytes(bs)) + if !quote { + b.Write(bs) + return + } + if len(bs) == 0 { + b.WriteString(`""`) + return + } + + var tmp [128]byte + b.Write(strconv.AppendQuote(tmp[:0], string(bs))) +} + +func (f *TextFormatter) appendNumeric(b *bytes.Buffer, out []byte) { + if f.ForceQuote { + var tmp [128]byte + b.Write(strconv.AppendQuote(tmp[:0], string(out))) + return } - for _, ch := range text { - //nolint:staticcheck // QF1001: could apply De Morgan's law - if !((ch >= 'a' && ch <= 'z') || - (ch >= 'A' && ch <= 'Z') || - (ch >= '0' && ch <= '9') || - ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') { + b.Write(out) +} + +func (f *TextFormatter) appendError(b *bytes.Buffer, v error) { + defer f.recoverValue(b, v, "Error") + + f.appendString(b, v.Error()) +} + +func (f *TextFormatter) appendStringer(b *bytes.Buffer, v fmt.Stringer) { + defer f.recoverValue(b, v, "String") + + f.appendString(b, v.String()) +} + +func (f *TextFormatter) recoverValue(b *bytes.Buffer, v any, method string) { + if r := recover(); r != nil { + rv := reflect.ValueOf(v) + if rv.Kind() == reflect.Pointer && rv.IsNil() { + f.appendString(b, "") + } else { + f.appendString(b, fmt.Sprintf("%%!v(PANIC=%s method: %v)", method, r)) + } + } +} + +// needsQuoting returns true if the string contains any byte that +// requires quoting. It returns false when every byte is "safe" according +// to isSafeByte. +func needsQuoting(s string) bool { + // use an index loop (avoid rune decoding). + for i := range len(s) { + c := s[i] + if !isSafeByte(c) { return true } } return false } -func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { - if b.Len() > 0 { - b.WriteByte(' ') +// needsQuotingBytes returns true if the byte slice contains any byte that +// requires quoting. It returns false when every byte is "safe" according +// to isSafeByte. +func needsQuotingBytes(bs []byte) bool { + for _, c := range bs { + if !isSafeByte(c) { + return true + } } - b.WriteString(key) - b.WriteByte('=') - f.appendValue(b, value) + return false } -func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { - stringVal, ok := value.(string) - if !ok { - stringVal = fmt.Sprint(value) +// isSafeByte returns true if the byte is allowed unquoted (ASCII and in the allowlist). +// It purposely uses byte arithmetic (no runes) for performance. +func isSafeByte(ch byte) bool { + ok := ch < 0x80 && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) + if ok { + return true } - - if !f.needsQuoting(stringVal) { - b.WriteString(stringVal) - } else { - fmt.Fprintf(b, "%q", stringVal) + switch ch { + case '-', '.', '_', '/', '@', '^', '+': + return true + default: + return false } } diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go index 074fd4b8..30e34cda 100644 --- a/vendor/github.com/sirupsen/logrus/writer.go +++ b/vendor/github.com/sirupsen/logrus/writer.go @@ -30,7 +30,7 @@ func (entry *Entry) Writer() *io.PipeWriter { func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { reader, writer := io.Pipe() - var printFunc func(args ...interface{}) + printFunc := entry.Print // Determine which log function to use based on the specified log level switch level { @@ -48,8 +48,6 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { printFunc = entry.Fatal case PanicLevel: printFunc = entry.Panic - default: - printFunc = entry.Print } // Start a new goroutine to scan the input and write it to the logger using the specified print function. @@ -63,7 +61,7 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { } // writerScanner scans the input from the reader and writes it to the logger -func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { +func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...any)) { scanner := bufio.NewScanner(reader) // Set the buffer size to the maximum token size to avoid buffer overflows diff --git a/vendor/github.com/sourcegraph/go-diff/diff/diff.go b/vendor/github.com/sourcegraph/go-diff/diff/diff.go index 81aa6557..cc19fe52 100644 --- a/vendor/github.com/sourcegraph/go-diff/diff/diff.go +++ b/vendor/github.com/sourcegraph/go-diff/diff/diff.go @@ -5,12 +5,18 @@ import ( "time" ) +// ParseOptions specifies options for parsing diffs. +type ParseOptions struct { + // KeepCR specifies whether to keep trailing carriage return characters (\r) in lines. + KeepCR bool +} + // A FileDiff represents a unified diff for a single file. // // A file unified diff has a header that resembles the following: // -// --- oldname 2009-10-11 15:12:20.000000000 -0700 -// +++ newname 2009-10-11 15:12:30.000000000 -0700 +// --- oldname 2009-10-11 15:12:20.000000000 -0700 +// +++ newname 2009-10-11 15:12:30.000000000 -0700 type FileDiff struct { // the original name of the file OrigName string diff --git a/vendor/github.com/sourcegraph/go-diff/diff/parse.go b/vendor/github.com/sourcegraph/go-diff/diff/parse.go index 48eeb967..b73e2301 100644 --- a/vendor/github.com/sourcegraph/go-diff/diff/parse.go +++ b/vendor/github.com/sourcegraph/go-diff/diff/parse.go @@ -1,7 +1,6 @@ package diff import ( - "bufio" "bytes" "errors" "fmt" @@ -17,13 +16,24 @@ import ( // case of per-file errors. If it cannot detect when the diff of the next file // begins, the hunks are added to the FileDiff of the previous file. func ParseMultiFileDiff(diff []byte) ([]*FileDiff, error) { - return NewMultiFileDiffReader(bytes.NewReader(diff)).ReadAllFiles() + return ParseMultiFileDiffOptions(diff, ParseOptions{}) +} + +// ParseMultiFileDiffOptions parses a multi-file unified diff with the given options. +func ParseMultiFileDiffOptions(diff []byte, opts ParseOptions) ([]*FileDiff, error) { + return NewMultiFileDiffReaderOptions(bytes.NewReader(diff), opts).ReadAllFiles() } // NewMultiFileDiffReader returns a new MultiFileDiffReader that reads // a multi-file unified diff from r. func NewMultiFileDiffReader(r io.Reader) *MultiFileDiffReader { - return &MultiFileDiffReader{reader: newLineReader(r)} + return NewMultiFileDiffReaderOptions(r, ParseOptions{}) +} + +// NewMultiFileDiffReaderOptions returns a new MultiFileDiffReader that reads +// a multi-file unified diff from r with the given options. +func NewMultiFileDiffReaderOptions(r io.Reader, opts ParseOptions) *MultiFileDiffReader { + return &MultiFileDiffReader{reader: newLineReaderOptions(r, opts)} } // MultiFileDiffReader reads a multi-file unified diff. @@ -153,13 +163,24 @@ func (r *MultiFileDiffReader) ReadAllFiles() ([]*FileDiff, error) { // ParseFileDiff parses a file unified diff. func ParseFileDiff(diff []byte) (*FileDiff, error) { - return NewFileDiffReader(bytes.NewReader(diff)).Read() + return ParseFileDiffOptions(diff, ParseOptions{}) +} + +// ParseFileDiffOptions parses a file unified diff with the given options. +func ParseFileDiffOptions(diff []byte, opts ParseOptions) (*FileDiff, error) { + return NewFileDiffReaderOptions(bytes.NewReader(diff), opts).Read() } // NewFileDiffReader returns a new FileDiffReader that reads a file // unified diff. func NewFileDiffReader(r io.Reader) *FileDiffReader { - return &FileDiffReader{reader: &lineReader{reader: bufio.NewReader(r)}} + return NewFileDiffReaderOptions(r, ParseOptions{}) +} + +// NewFileDiffReaderOptions returns a new FileDiffReader that reads a file +// unified diff with the given options. +func NewFileDiffReaderOptions(r io.Reader, opts ParseOptions) *FileDiffReader { + return &FileDiffReader{reader: newLineReaderOptions(r, opts)} } // FileDiffReader reads a unified file diff. @@ -405,6 +426,7 @@ func readQuotedFilename(text string) (value string, remainder string, err error) // valid syntax, it may be impossible to extract filenames; if so, the // function returns ("", "", true). func parseDiffGitArgs(diffArgs string) (string, string, bool) { + diffArgs = strings.TrimSuffix(diffArgs, "\r") length := len(diffArgs) if length < 3 { return "", "", false @@ -540,6 +562,7 @@ func handleEmpty(fd *FileDiff) (wasEmpty bool) { return } rawFilename := header[len(prefix):] + rawFilename = strings.TrimSuffix(rawFilename, "\r") // extract the filename prefix (e.g. "a/") from the 'diff --git' line. var prefixLetterIndex int @@ -586,7 +609,12 @@ var ( // only of hunks and not include a file header; if it has a file // header, use ParseFileDiff. func ParseHunks(diff []byte) ([]*Hunk, error) { - r := NewHunksReader(bytes.NewReader(diff)) + return ParseHunksOptions(diff, ParseOptions{}) +} + +// ParseHunksOptions parses hunks from a unified diff with the given options. +func ParseHunksOptions(diff []byte, opts ParseOptions) ([]*Hunk, error) { + r := NewHunksReaderOptions(bytes.NewReader(diff), opts) hunks, err := r.ReadAllHunks() if err != nil { return nil, err @@ -597,7 +625,13 @@ func ParseHunks(diff []byte) ([]*Hunk, error) { // NewHunksReader returns a new HunksReader that reads unified diff hunks // from r. func NewHunksReader(r io.Reader) *HunksReader { - return &HunksReader{reader: &lineReader{reader: bufio.NewReader(r)}} + return NewHunksReaderOptions(r, ParseOptions{}) +} + +// NewHunksReaderOptions returns a new HunksReader that reads unified diff hunks +// from r with the given options. +func NewHunksReaderOptions(r io.Reader, opts ParseOptions) *HunksReader { + return &HunksReader{reader: newLineReaderOptions(r, opts)} } // A HunksReader reads hunks from a unified diff. @@ -701,7 +735,7 @@ func (r *HunksReader) ReadHunk() (*Hunk, error) { // handle that case. return r.hunk, &ParseError{r.line, r.offset, &ErrBadHunkLine{Line: line}} } - if bytes.Equal(line, []byte(noNewlineMessage)) { + if bytes.Equal(bytes.TrimSuffix(line, []byte("\r")), []byte(noNewlineMessage)) { if lastLineFromOrig { // Retain the newline in the body (otherwise the // diff line would be like "-a+b", where "+b" is @@ -755,6 +789,7 @@ func linePrefix(c byte) bool { // if its value is 1. normalizeHeader returns an error if the header // is not in the correct format. func normalizeHeader(header string) (string, string, error) { + header = strings.TrimSuffix(header, "\r") // Split the header into five parts: the first '@@', the two // ranges, the last '@@', and the optional section. pieces := strings.SplitN(header, " ", 5) @@ -815,7 +850,8 @@ func parseOnlyInMessage(line []byte) (bool, []byte, []byte) { if idx < 0 { return false, nil, nil } - return true, line[:idx], line[idx+2:] + filename := bytes.TrimSuffix(line[idx+2:], []byte("\r")) + return true, line[:idx], filename } // A ParseError is a description of a unified diff syntax error. diff --git a/vendor/github.com/sourcegraph/go-diff/diff/reader_util.go b/vendor/github.com/sourcegraph/go-diff/diff/reader_util.go index 45300252..3356283d 100644 --- a/vendor/github.com/sourcegraph/go-diff/diff/reader_util.go +++ b/vendor/github.com/sourcegraph/go-diff/diff/reader_util.go @@ -13,6 +13,13 @@ func newLineReader(r io.Reader) *lineReader { return &lineReader{reader: bufio.NewReader(r)} } +func newLineReaderOptions(r io.Reader, opts ParseOptions) *lineReader { + return &lineReader{ + reader: bufio.NewReader(r), + keepCR: opts.KeepCR, + } +} + // lineReader is a wrapper around a bufio.Reader that caches the next line to // provide lookahead functionality for the next two lines. type lineReader struct { @@ -20,14 +27,20 @@ type lineReader struct { cachedNextLine []byte cachedNextLineErr error + + keepCR bool +} + +func (l *lineReader) ensureCachedNextLine() { + if l.cachedNextLine == nil && l.cachedNextLineErr == nil { + l.cachedNextLine, l.cachedNextLineErr = readLine(l.reader, l.keepCR) + } } // readLine returns the next unconsumed line and advances the internal cache of // the lineReader. func (l *lineReader) readLine() ([]byte, error) { - if l.cachedNextLine == nil && l.cachedNextLineErr == nil { - l.cachedNextLine, l.cachedNextLineErr = readLine(l.reader) - } + l.ensureCachedNextLine() if l.cachedNextLineErr != nil { return nil, l.cachedNextLineErr @@ -35,7 +48,7 @@ func (l *lineReader) readLine() ([]byte, error) { next := l.cachedNextLine - l.cachedNextLine, l.cachedNextLineErr = readLine(l.reader) + l.cachedNextLine, l.cachedNextLineErr = readLine(l.reader, l.keepCR) return next, nil } @@ -46,9 +59,7 @@ func (l *lineReader) readLine() ([]byte, error) { // io.EOF and bufio.ErrBufferFull errors are ignored so that the function can // be used when at the end of the file. func (l *lineReader) nextLineStartsWith(prefix string) (bool, error) { - if l.cachedNextLine == nil && l.cachedNextLineErr == nil { - l.cachedNextLine, l.cachedNextLineErr = readLine(l.reader) - } + l.ensureCachedNextLine() return l.lineHasPrefix(l.cachedNextLine, prefix, l.cachedNextLineErr) } @@ -58,14 +69,8 @@ func (l *lineReader) nextLineStartsWith(prefix string) (bool, error) { // // io.EOF and bufio.ErrBufferFull errors are ignored so that the function can // be used when at the end of the file. -// -// The lineReader MUST be initialized by calling readLine at least once before -// calling nextLineStartsWith. Otherwise ErrLineReaderUninitialized will be -// returned. func (l *lineReader) nextNextLineStartsWith(prefix string) (bool, error) { - if l.cachedNextLine == nil && l.cachedNextLineErr == nil { - l.cachedNextLine, l.cachedNextLineErr = readLine(l.reader) - } + l.ensureCachedNextLine() next, err := l.reader.Peek(len(prefix)) return l.lineHasPrefix(next, prefix, err) @@ -93,22 +98,21 @@ func (l *lineReader) lineHasPrefix(line []byte, prefix string, readErr error) (b // the next line in the Reader with the trailing newline stripped. It will return an // io.EOF error when there is nothing left to read (at the start of the function call). It // will return any other errors it receives from the underlying call to ReadBytes. -func readLine(r *bufio.Reader) ([]byte, error) { - line_, err := r.ReadBytes('\n') - if err == io.EOF { - if len(line_) == 0 { - return nil, io.EOF - } - - // ReadBytes returned io.EOF, because it didn't find another newline, but there is - // still the remainder of the file to return as a line. - line := line_ - return line, nil - } else if err != nil { +func readLine(r *bufio.Reader, keepCR bool) ([]byte, error) { + line, err := r.ReadBytes('\n') + if err == io.EOF && len(line) == 0 { + return nil, io.EOF + } + if err != nil && err != io.EOF { return nil, err } - line := line_[0 : len(line_)-1] - return dropCR(line), nil + if line[len(line)-1] == '\n' { + line = line[:len(line)-1] + } + if !keepCR { + return dropCR(line), nil + } + return line, nil } // dropCR drops a terminal \r from the data. diff --git a/vendor/github.com/sourcegraph/go-diff/diff/reverse.go b/vendor/github.com/sourcegraph/go-diff/diff/reverse.go new file mode 100644 index 00000000..87715efb --- /dev/null +++ b/vendor/github.com/sourcegraph/go-diff/diff/reverse.go @@ -0,0 +1,192 @@ +package diff + +import ( + "bytes" + "errors" + "fmt" +) + +// ReverseFileDiff takes a diff.FileDiff, and returns the reverse operation. +// This is a FileDiff that undoes the edit of the original. +func ReverseFileDiff(fd *FileDiff) (*FileDiff, error) { + reverse := FileDiff{ + OrigName: fd.NewName, + OrigTime: fd.NewTime, + NewName: fd.OrigName, + NewTime: fd.OrigTime, + Extended: fd.Extended, + } + for _, hunk := range fd.Hunks { + invHunk, err := reverseHunk(hunk) + if err != nil { + return nil, err + } + reverse.Hunks = append(reverse.Hunks, invHunk) + } + return &reverse, nil +} + +// ReverseMultiFileDiff reverses a series of FileDiffs. +func ReverseMultiFileDiff(fds []*FileDiff) ([]*FileDiff, error) { + var reverse []*FileDiff + for _, fd := range fds { + r, err := ReverseFileDiff(fd) + if err != nil { + return nil, err + } + reverse = append(reverse, r) + } + return reverse, nil +} + +// A subhunk represents a portion of a Hunk.Body, split into three sections. +// It consists of zero or more context lines, followed by zero or more orig +// lines and then zero or more new lines. +// +// Each line is stored WITHOUT its starting character, but with the newlines +// included. The final entry in a section may be missing a trailing newline. +// +// A missing newline in orig is represented in a Hunk by OrigNoNewlineAt, +// but is represented here as a missing newline. +type contextLine struct { + body []byte + bare bool +} + +type subhunk struct { + context []contextLine + orig [][]byte + new [][]byte +} + +// reverseHunk converts a Hunk into its reverse operation. +func reverseHunk(forward *Hunk) (*Hunk, error) { + reverse := Hunk{ + OrigStartLine: forward.NewStartLine, + OrigLines: forward.NewLines, + OrigNoNewlineAt: 0, // we may change this below + NewStartLine: forward.OrigStartLine, + NewLines: forward.OrigLines, + Section: forward.Section, + StartPosition: forward.StartPosition, + } + subs, err := toSubhunks(forward) + if err != nil { + return nil, err + } + for _, sub := range subs { + invSub := subhunk{ + context: sub.context, + orig: sub.new, + new: sub.orig, + } + for _, line := range invSub.context { + if line.bare { + reverse.Body = append(reverse.Body, line.body...) + continue + } + reverse.Body = append(reverse.Body, ' ') + reverse.Body = append(reverse.Body, line.body...) + } + for _, line := range invSub.orig { + reverse.Body = append(reverse.Body, '-') + reverse.Body = append(reverse.Body, line...) + } + if len(invSub.orig) > 0 && reverse.Body[len(reverse.Body)-1] != '\n' { + // There was a missing newline in `orig`, which we encode in a + // hunk with an offset. + reverse.Body = append(reverse.Body, '\n') + reverse.OrigNoNewlineAt = int32(len(reverse.Body)) + } + for _, line := range invSub.new { + reverse.Body = append(reverse.Body, '+') + reverse.Body = append(reverse.Body, line...) + } + } + return &reverse, nil +} + +func extractContextLines(from *[]byte) []contextLine { + var lines []contextLine + for len(*from) > 0 { + if (*from)[0] == '\n' { + lines = append(lines, contextLine{body: []byte{'\n'}, bare: true}) + *from = (*from)[1:] + continue + } + if (*from)[0] != ' ' { + break + } + + newline := bytes.IndexByte(*from, '\n') + if newline < 0 { + lines = append(lines, contextLine{body: (*from)[1:]}) + *from = nil + continue + } + + lines = append(lines, contextLine{body: (*from)[1 : newline+1]}) + *from = (*from)[newline+1:] + } + return lines +} + +func extractLinesStartingWith(from *[]byte, startingWith byte) [][]byte { + var lines [][]byte + for len(*from) > 0 { + if (*from)[0] != startingWith { + break + } + + newline := bytes.IndexByte(*from, '\n') + if newline < 0 { + lines = append(lines, (*from)[1:]) + *from = nil + continue + } + + lines = append(lines, (*from)[1:newline+1]) + *from = (*from)[newline+1:] + } + return lines +} + +// Extracts the subhunks from a diff.Hunk. +// +// This groups a Hunk's buffer into one or more subhunks, matching the conditions +// of `subhunk` above. This function groups, strips prefix characters, and strips +// a newline for `OrigNoNewlineAt` if necessary. +func toSubhunks(hunk *Hunk) ([]subhunk, error) { + var body []byte = hunk.Body + var subhunks []subhunk + if len(body) == 0 { + return nil, nil + } + for len(body) > 0 { + sh := subhunk{ + context: extractContextLines(&body), + orig: extractLinesStartingWith(&body, '-'), + new: extractLinesStartingWith(&body, '+'), + } + if len(sh.context) == 0 && len(sh.orig) == 0 && len(sh.new) == 0 { + // The first line didn't start with any expected prefix. + return nil, fmt.Errorf("unexpected character %q at start of line", body[0]) + } + subhunks = append(subhunks, sh) + } + if hunk.OrigNoNewlineAt > 0 { + // The Hunk represents a missing newline at the end of an "orig" line with a + // OrigNoNewlineAt index. We represent it here as an actual missing newline. + var lastSubhunk *subhunk = &subhunks[len(subhunks)-1] + s := len(lastSubhunk.orig) + if s == 0 { + return nil, errors.New("inconsistent OrigNoNewlineAt in input") + } + var cut bool + lastSubhunk.orig[s-1], cut = bytes.CutSuffix(lastSubhunk.orig[s-1], []byte("\n")) + if !cut { + return nil, errors.New("missing newline in input") + } + } + return subhunks, nil +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index c592f6ad..a19a8927 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -84,7 +84,7 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar return Equal(t, expected, actual, append([]interface{}{msg}, args...)...) } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -124,7 +124,7 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // assert.Errorf(t, err, "error message %s", "formatted") @@ -144,8 +144,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") @@ -190,10 +190,10 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // time.Sleep(8*time.Second) // externalValue = true // }() -// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// assert.EventuallyWithTf(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") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") 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() @@ -552,7 +552,7 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool return NoDirExists(t, path, append([]interface{}{msg}, args...)...) } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if assert.NoErrorf(t, err, "error message %s", "formatted") { @@ -849,7 +849,19 @@ func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...) } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// assert.YAMLEqf(t, expected, actual, "error message %s", "formatted") func YAMLEqf(t TestingT, expected string, actual string, 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 58db9284..cd2a8606 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -146,7 +146,7 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs return Equal(a.t, expected, actual, msgAndArgs...) } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -158,7 +158,7 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... return EqualError(a.t, theError, errString, msgAndArgs...) } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -240,7 +240,7 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string return Equalf(a.t, expected, actual, msg, args...) } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // a.Error(err) @@ -269,8 +269,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. return ErrorAsf(a.t, err, target, msg, args...) } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContains(err, expectedErrorSubString) @@ -281,8 +281,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . return ErrorContains(a.t, theError, contains, msgAndArgs...) } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") @@ -311,7 +311,7 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter return ErrorIsf(a.t, err, target, msg, args...) } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // a.Errorf(err, "error message %s", "formatted") @@ -372,10 +372,10 @@ func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor // time.Sleep(8*time.Second) // externalValue = true // }() -// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// a.EventuallyWithTf(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") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") 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() @@ -1096,7 +1096,7 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) return NoDirExistsf(a.t, path, msg, args...) } -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if a.NoError(err) { @@ -1109,7 +1109,7 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { return NoError(a.t, err, msgAndArgs...) } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if a.NoErrorf(err, "error message %s", "formatted") { @@ -1690,7 +1690,19 @@ func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Ti return WithinRangef(a.t, actual, start, end, msg, args...) } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEq(expected, actual) func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1698,7 +1710,19 @@ func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interf return YAMLEq(a.t, expected, actual, msgAndArgs...) } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEqf(expected, actual, "error message %s", "formatted") func (a *Assertions) YAMLEqf(expected string, actual string, 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 2fdf80fd..a44b40ed 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -9,7 +9,7 @@ import ( func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { objKind := reflect.TypeOf(object).Kind() if objKind != reflect.Slice && objKind != reflect.Array { - return false + return Fail(t, fmt.Sprintf("object %T is not an ordered collection", object), msgAndArgs...) } objValue := reflect.ValueOf(object) @@ -50,6 +50,9 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareR // assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } @@ -59,6 +62,9 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } @@ -68,6 +74,9 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } @@ -77,5 +86,8 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{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 de8de0cb..166f6372 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -17,11 +17,10 @@ import ( "unicode" "unicode/utf8" - "github.com/davecgh/go-spew/spew" - "github.com/pmezard/go-difflib/difflib" - - // Wrapper around gopkg.in/yaml.v3 + // Wrapper around go.yaml.in/yaml/v3 "github.com/stretchr/testify/assert/yaml" + "github.com/stretchr/testify/internal/difflib" + "github.com/stretchr/testify/internal/spew" ) //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" @@ -33,19 +32,19 @@ type TestingT interface { // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful // for table driven tests. -type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool +type ComparisonAssertionFunc = func(TestingT, interface{}, interface{}, ...interface{}) bool // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful // for table driven tests. -type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool +type ValueAssertionFunc = func(TestingT, interface{}, ...interface{}) bool // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful // for table driven tests. -type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool +type BoolAssertionFunc = func(TestingT, bool, ...interface{}) bool // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful // for table driven tests. -type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool +type ErrorAssertionFunc = func(TestingT, error, ...interface{}) bool // PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful // for table driven tests. @@ -325,13 +324,15 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { func indentMessageLines(message string, longestLabelLen int) string { outBuf := new(bytes.Buffer) - for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { - // no need to align first line because it starts at the correct location (after the label) - if i != 0 { - // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab - outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") + scanner := bufio.NewScanner(strings.NewReader(message)) + for firstLine := true; scanner.Scan(); firstLine = false { + if !firstLine { + fmt.Fprint(outBuf, "\n\t"+strings.Repeat(" ", longestLabelLen+1)+"\t") } - outBuf.WriteString(scanner.Text()) + fmt.Fprint(outBuf, scanner.Text()) + } + if err := scanner.Err(); err != nil { + return fmt.Sprintf("cannot display message: %s", err) } return outBuf.String() @@ -544,9 +545,8 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b if !same { // both are pointers but not the same type & pointing to the same address return Fail(t, fmt.Sprintf("Not same: \n"+ - "expected: %p %#[1]v\n"+ - "actual : %p %#[2]v", - expected, actual), msgAndArgs...) + "expected: %[2]s (%[1]T)(%[1]p)\n"+ + "actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...) } return true @@ -571,8 +571,8 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} if same { return Fail(t, fmt.Sprintf( - "Expected and actual point to the same object: %p %#[1]v", - expected), msgAndArgs...) + "Expected and actual point to the same object: %p %s", + expected, truncatingFormat("%#v", expected)), msgAndArgs...) } return true } @@ -604,25 +604,26 @@ func samePointers(first, second interface{}) (same bool, ok bool) { // to a type conversion in the Go grammar. func formatUnequalValues(expected, actual interface{}) (e string, a string) { if reflect.TypeOf(expected) != reflect.TypeOf(actual) { - return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)), - fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual)) + return fmt.Sprintf("%T(%s)", expected, truncatingFormat("%#v", expected)), + fmt.Sprintf("%T(%s)", actual, truncatingFormat("%#v", actual)) } switch expected.(type) { case time.Duration: return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) } - return truncatingFormat(expected), truncatingFormat(actual) + return truncatingFormat("%#v", expected), truncatingFormat("%#v", actual) } // truncatingFormat formats the data and truncates it if it's too long. // // This helps keep formatted error messages lines from exceeding the // bufio.MaxScanTokenSize max line length that the go testing framework imposes. -func truncatingFormat(data interface{}) string { - value := fmt.Sprintf("%#v", data) - max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed. - if len(value) > max { - value = value[0:max] + "<... truncated>" +func truncatingFormat(format string, data interface{}) string { + value := fmt.Sprintf(format, data) + // Give us space for two truncated objects and the surrounding sentence. + maxMessageSize := bufio.MaxScanTokenSize/2 - 100 + if len(value) > maxMessageSize { + value = value[0:maxMessageSize] + "<... truncated>" } return value } @@ -743,7 +744,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) + return Fail(t, fmt.Sprintf("Expected nil, but got: %s", truncatingFormat("%#v", object)), msgAndArgs...) } // isEmpty gets whether the specified object is considered empty or not. @@ -793,7 +794,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) + Fail(t, fmt.Sprintf("Should be empty, but was %s", truncatingFormat("%v", object)), msgAndArgs...) } return pass @@ -836,11 +837,11 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) } l, ok := getLen(object) if !ok { - return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", truncatingFormat("%v", object)), msgAndArgs...) } if l != length { - return Fail(t, fmt.Sprintf("\"%v\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q should have %d item(s), but has %d", truncatingFormat("%v", object), length, l), msgAndArgs...) } return true } @@ -889,7 +890,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ } if ObjectsAreEqual(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } return true @@ -904,7 +905,7 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte } if ObjectsAreEqualValues(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } return true @@ -964,10 +965,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...) } if !found { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) } return true @@ -986,10 +987,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...) } if found { - return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) } return true @@ -1031,10 +1032,10 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok av := actualMap.MapIndex(k) if !av.IsValid() { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) } if !ObjectsAreEqual(ev.Interface(), av.Interface()) { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) } } @@ -1056,7 +1057,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) } if !found { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", list), element), msgAndArgs...) } } @@ -1082,12 +1083,12 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) listKind := reflect.TypeOf(list).Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", list, listKind), msgAndArgs...) } subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { @@ -1106,7 +1107,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) } } - return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) } subsetList := reflect.ValueOf(subset) @@ -1121,14 +1122,14 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { - return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) } if !found { return true } } - return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) } // ElementsMatch asserts that the specified listA(array, slice...) is equal to specified @@ -1343,9 +1344,15 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs . if !funcDidPanic { return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) } - panicErr, ok := panicValue.(error) - if !ok || panicErr.Error() != errString { - return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...) + panicErr, isError := panicValue.(error) + if !isError || panicErr.Error() != errString { + msg := fmt.Sprintf("func %#v should panic with error message:\t%#v\n", f, errString) + if isError { + msg += fmt.Sprintf("\tError message:\t%#v\n", panicErr.Error()) + } + msg += fmt.Sprintf("\tPanic value:\t%#v\n", panicValue) + msg += fmt.Sprintf("\tPanic stack:\t%s\n", panickedStack) + return Fail(t, msg, msgAndArgs...) } return true @@ -1624,7 +1631,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m Errors */ -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if assert.NoError(t, err) { @@ -1635,13 +1642,13 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) + return Fail(t, fmt.Sprintf("Received unexpected error:\n%s", truncatingFormat("%+v", err)), msgAndArgs...) } return true } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // assert.Error(t, err) @@ -1656,7 +1663,7 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { return true } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -1674,13 +1681,13 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte if expected != actual { return Fail(t, fmt.Sprintf("Error message not equal:\n"+ "expected: %q\n"+ - "actual : %q", expected, actual), msgAndArgs...) + "actual : %s", expected, truncatingFormat("%q", actual)), msgAndArgs...) } return true } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // assert.ErrorContains(t, err, expectedErrorSubString) @@ -1694,7 +1701,7 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in actual := theError.Error() if !strings.Contains(actual, contains) { - return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("Error %s does not contain %#v", truncatingFormat("%#v", actual), contains), msgAndArgs...) } return true @@ -1760,7 +1767,7 @@ func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { h.Helper() } if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { - return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) + return Fail(t, fmt.Sprintf("Should be zero, but was %s", truncatingFormat("%v", i)), msgAndArgs...) } return true } @@ -1874,7 +1881,19 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// assert.YAMLEq(t, expected, actual) func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -2188,8 +2207,8 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ - "expected: %q\n"+ - "in chain: %s", expectedText, chain, + "expected: %s\n"+ + "in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), ), msgAndArgs...) } @@ -2211,8 +2230,8 @@ func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ - "found: %q\n"+ - "in chain: %s", expectedText, chain, + "found: %s\n"+ + "in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), ), msgAndArgs...) } @@ -2236,7 +2255,7 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{ return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ "expected: %s\n"+ - "in chain: %s", expectedType, chain, + "in chain: %s", expectedType, truncatingFormat("%s", chain), ), msgAndArgs...) } @@ -2254,7 +2273,7 @@ func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interfa return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ "found: %s\n"+ - "in chain: %s", reflect.TypeOf(target).Elem().String(), chain, + "in chain: %s", reflect.TypeOf(target).Elem().String(), truncatingFormat("%s", chain), ), msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go index a0b953aa..c111589c 100644 --- a/vendor/github.com/stretchr/testify/assert/doc.go +++ b/vendor/github.com/stretchr/testify/assert/doc.go @@ -40,8 +40,8 @@ // // # 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 +// 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 // testing framework. This allows the assertion funcs to write the failings and other details to // the correct place. // diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go index 5a74c4f4..956227ca 100644 --- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go @@ -7,7 +7,7 @@ // go test -tags testify_yaml_custom // // This implementation can be used at build time to replace the default implementation -// to avoid linking with [gopkg.in/yaml.v3]. +// to avoid linking with [go.yaml.in/yaml/v3]. // // In your test package: // diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go index 0bae80e3..dd89ac03 100644 --- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go @@ -6,7 +6,7 @@ // indirection with an alternative implementation of this package that uses // another implementation of YAML deserialization. This allows to not either not // use YAML deserialization at all, or to use another implementation than -// [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]). +// [go.yaml.in/yaml/v3] (for example for license compatibility reasons, see [PR #1120]). // // Alternative implementations are selected using build tags: // @@ -28,9 +28,9 @@ // [PR #1120]: https://github.com/stretchr/testify/pull/1120 package yaml -import goyaml "gopkg.in/yaml.v3" +import goyaml "go.yaml.in/yaml/v3" -// Unmarshal is just a wrapper of [gopkg.in/yaml.v3.Unmarshal]. +// Unmarshal is just a wrapper of [go.yaml.in/yaml/v3.Unmarshal]. func Unmarshal(in []byte, out interface{}) error { return goyaml.Unmarshal(in, out) } diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go index 8041803f..a51d2792 100644 --- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go @@ -3,7 +3,7 @@ // Package yaml is an implementation of YAML functions that always fail. // // This implementation can be used at build time to replace the default implementation -// to avoid linking with [gopkg.in/yaml.v3]: +// to avoid linking with [go.yaml.in/yaml/v3]: // // go test -tags testify_yaml_fail package yaml diff --git a/vendor/github.com/pmezard/go-difflib/LICENSE b/vendor/github.com/stretchr/testify/internal/difflib/LICENSE similarity index 95% rename from vendor/github.com/pmezard/go-difflib/LICENSE rename to vendor/github.com/stretchr/testify/internal/difflib/LICENSE index c67dad61..485be13c 100644 --- a/vendor/github.com/pmezard/go-difflib/LICENSE +++ b/vendor/github.com/stretchr/testify/internal/difflib/LICENSE @@ -24,4 +24,4 @@ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go b/vendor/github.com/stretchr/testify/internal/difflib/difflib.go similarity index 77% rename from vendor/github.com/pmezard/go-difflib/difflib/difflib.go rename to vendor/github.com/stretchr/testify/internal/difflib/difflib.go index 003e99fa..9984599b 100644 --- a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go +++ b/vendor/github.com/stretchr/testify/internal/difflib/difflib.go @@ -8,11 +8,14 @@ // // - unified_diff // -// - context_diff -// // Getting unified diffs was the main goal of the port. Keep in mind this code // is mostly suitable to output text differences in a human friendly way, there // are no guarantees generated diffs are consumable by patch(1). +// +// This package was adopted from [github.com/pmezard/go-difflib] which +// is no longer maintained. +// +// [github.com/pmezard/go-difflib]: https://github.com/pmezard/go-difflib package difflib import ( @@ -37,13 +40,6 @@ func max(a, b int) int { return b } -func calculateRatio(matches, length int) float64 { - if length > 0 { - return 2.0 * float64(matches) / float64(length) - } - return 1.0 -} - type Match struct { A int B int @@ -103,14 +99,6 @@ func NewMatcher(a, b []string) *SequenceMatcher { return &m } -func NewMatcherWithJunk(a, b []string, autoJunk bool, - isJunk func(string) bool) *SequenceMatcher { - - m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk} - m.SetSeqs(a, b) - return &m -} - // Set two sequences to be compared. func (m *SequenceMatcher) SetSeqs(a, b []string) { m.SetSeq1(a) @@ -199,12 +187,15 @@ func (m *SequenceMatcher) isBJunk(s string) bool { // If IsJunk is not defined: // // Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where -// alo <= i <= i+k <= ahi -// blo <= j <= j+k <= bhi +// +// alo <= i <= i+k <= ahi +// blo <= j <= j+k <= bhi +// // and for all (i',j',k') meeting those conditions, -// k >= k' -// i <= i' -// and if i == i', j <= j' +// +// k >= k' +// i <= i' +// and if i == i', j <= j' // // In other words, of all maximal matching blocks, return one that // starts earliest in a, and of all those maximal matching blocks that @@ -451,66 +442,6 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { return groups } -// Return a measure of the sequences' similarity (float in [0,1]). -// -// Where T is the total number of elements in both sequences, and -// M is the number of matches, this is 2.0*M / T. -// Note that this is 1 if the sequences are identical, and 0 if -// they have nothing in common. -// -// .Ratio() is expensive to compute if you haven't already computed -// .GetMatchingBlocks() or .GetOpCodes(), in which case you may -// want to try .QuickRatio() or .RealQuickRation() first to get an -// upper bound. -func (m *SequenceMatcher) Ratio() float64 { - matches := 0 - for _, m := range m.GetMatchingBlocks() { - matches += m.Size - } - return calculateRatio(matches, len(m.a)+len(m.b)) -} - -// Return an upper bound on ratio() relatively quickly. -// -// This isn't defined beyond that it is an upper bound on .Ratio(), and -// is faster to compute. -func (m *SequenceMatcher) QuickRatio() float64 { - // viewing a and b as multisets, set matches to the cardinality - // of their intersection; this counts the number of matches - // without regard to order, so is clearly an upper bound - if m.fullBCount == nil { - m.fullBCount = map[string]int{} - for _, s := range m.b { - m.fullBCount[s] = m.fullBCount[s] + 1 - } - } - - // avail[x] is the number of times x appears in 'b' less the - // number of times we've seen it in 'a' so far ... kinda - avail := map[string]int{} - matches := 0 - for _, s := range m.a { - n, ok := avail[s] - if !ok { - n = m.fullBCount[s] - } - avail[s] = n - 1 - if n > 0 { - matches += 1 - } - } - return calculateRatio(matches, len(m.a)+len(m.b)) -} - -// Return an upper bound on ratio() very quickly. -// -// This isn't defined beyond that it is an upper bound on .Ratio(), and -// is faster to compute than either .Ratio() or .QuickRatio(). -func (m *SequenceMatcher) RealQuickRatio() float64 { - la, lb := len(m.a), len(m.b) - return calculateRatio(min(la, lb), la+lb) -} - // Convert range to the "ed" format func formatRangeUnified(start, stop int) string { // Per the diff spec at http://www.unix.org/single_unix_specification/ @@ -652,117 +583,6 @@ func formatRangeContext(start, stop int) string { return fmt.Sprintf("%d,%d", beginning, beginning+length-1) } -type ContextDiff UnifiedDiff - -// Compare two sequences of lines; generate the delta as a context diff. -// -// Context diffs are a compact way of showing line changes and a few -// lines of context. The number of context lines is set by diff.Context -// which defaults to three. -// -// By default, the diff control lines (those with *** or ---) are -// created with a trailing newline. -// -// For inputs that do not have trailing newlines, set the diff.Eol -// argument to "" so that the output will be uniformly newline free. -// -// The context diff format normally has a header for filenames and -// modification times. Any or all of these may be specified using -// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. -// The modification times are normally expressed in the ISO 8601 format. -// If not specified, the strings default to blanks. -func WriteContextDiff(writer io.Writer, diff ContextDiff) error { - buf := bufio.NewWriter(writer) - defer buf.Flush() - var diffErr error - wf := func(format string, args ...interface{}) { - _, err := buf.WriteString(fmt.Sprintf(format, args...)) - if diffErr == nil && err != nil { - diffErr = err - } - } - ws := func(s string) { - _, err := buf.WriteString(s) - if diffErr == nil && err != nil { - diffErr = err - } - } - - if len(diff.Eol) == 0 { - diff.Eol = "\n" - } - - prefix := map[byte]string{ - 'i': "+ ", - 'd': "- ", - 'r': "! ", - 'e': " ", - } - - started := false - m := NewMatcher(diff.A, diff.B) - for _, g := range m.GetGroupedOpCodes(diff.Context) { - if !started { - started = true - fromDate := "" - if len(diff.FromDate) > 0 { - fromDate = "\t" + diff.FromDate - } - toDate := "" - if len(diff.ToDate) > 0 { - toDate = "\t" + diff.ToDate - } - if diff.FromFile != "" || diff.ToFile != "" { - wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol) - wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol) - } - } - - first, last := g[0], g[len(g)-1] - ws("***************" + diff.Eol) - - range1 := formatRangeContext(first.I1, last.I2) - wf("*** %s ****%s", range1, diff.Eol) - for _, c := range g { - if c.Tag == 'r' || c.Tag == 'd' { - for _, cc := range g { - if cc.Tag == 'i' { - continue - } - for _, line := range diff.A[cc.I1:cc.I2] { - ws(prefix[cc.Tag] + line) - } - } - break - } - } - - range2 := formatRangeContext(first.J1, last.J2) - wf("--- %s ----%s", range2, diff.Eol) - for _, c := range g { - if c.Tag == 'r' || c.Tag == 'i' { - for _, cc := range g { - if cc.Tag == 'd' { - continue - } - for _, line := range diff.B[cc.J1:cc.J2] { - ws(prefix[cc.Tag] + line) - } - } - break - } - } - } - return diffErr -} - -// Like WriteContextDiff but returns the diff a string. -func GetContextDiffString(diff ContextDiff) (string, error) { - w := &bytes.Buffer{} - err := WriteContextDiff(w, diff) - return string(w.Bytes()), err -} - // Split a string on "\n" while preserving them. The output can be used // as input for UnifiedDiff and ContextDiff structures. func SplitLines(s string) []string { diff --git a/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/stretchr/testify/internal/spew/LICENSE similarity index 100% rename from vendor/github.com/davecgh/go-spew/LICENSE rename to vendor/github.com/stretchr/testify/internal/spew/LICENSE diff --git a/vendor/github.com/stretchr/testify/internal/spew/README.md b/vendor/github.com/stretchr/testify/internal/spew/README.md new file mode 100644 index 00000000..51a909e2 --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/README.md @@ -0,0 +1,12 @@ +go-spew +======= + +[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) + +Go-spew implements a deep pretty printer for Go data structures to aid in +debugging. A comprehensive suite of tests with 100% test coverage is provided +to ensure proper functionality. + +## License + +Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/stretchr/testify/internal/spew/bypass.go similarity index 98% rename from vendor/github.com/davecgh/go-spew/spew/bypass.go rename to vendor/github.com/stretchr/testify/internal/spew/bypass.go index 79299478..70ddeaad 100644 --- a/vendor/github.com/davecgh/go-spew/spew/bypass.go +++ b/vendor/github.com/stretchr/testify/internal/spew/bypass.go @@ -18,6 +18,7 @@ // tag is deprecated and thus should not be used. // Go versions prior to 1.4 are disabled because they use a different layout // for interfaces which make the implementation of unsafeReflectValue more complex. +//go:build !js && !appengine && !safe && !disableunsafe && go1.4 // +build !js,!appengine,!safe,!disableunsafe,go1.4 package spew diff --git a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/stretchr/testify/internal/spew/bypasssafe.go similarity index 96% rename from vendor/github.com/davecgh/go-spew/spew/bypasssafe.go rename to vendor/github.com/stretchr/testify/internal/spew/bypasssafe.go index 205c28d6..5e2d890d 100644 --- a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go +++ b/vendor/github.com/stretchr/testify/internal/spew/bypasssafe.go @@ -16,6 +16,7 @@ // when the code is running on Google App Engine, compiled by GopherJS, or // "-tags safe" is added to the go build command line. The "disableunsafe" // tag is deprecated and thus should not be used. +//go:build js || appengine || safe || disableunsafe || !go1.4 // +build js appengine safe disableunsafe !go1.4 package spew diff --git a/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/stretchr/testify/internal/spew/common.go similarity index 100% rename from vendor/github.com/davecgh/go-spew/spew/common.go rename to vendor/github.com/stretchr/testify/internal/spew/common.go diff --git a/vendor/github.com/davecgh/go-spew/spew/config.go b/vendor/github.com/stretchr/testify/internal/spew/config.go similarity index 95% rename from vendor/github.com/davecgh/go-spew/spew/config.go rename to vendor/github.com/stretchr/testify/internal/spew/config.go index 2e3d22f3..161895fc 100644 --- a/vendor/github.com/davecgh/go-spew/spew/config.go +++ b/vendor/github.com/stretchr/testify/internal/spew/config.go @@ -254,15 +254,15 @@ pointer addresses used to indirect to the final value. It provides the following features over the built-in printing facilities provided by the fmt package: - * Pointers are dereferenced and followed - * Circular data structures are detected and handled properly - * Custom Stringer/error interfaces are optionally invoked, including - on unexported types - * Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - * Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output + - Pointers are dereferenced and followed + - Circular data structures are detected and handled properly + - Custom Stringer/error interfaces are optionally invoked, including + on unexported types + - Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + - Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output The configuration options are controlled by modifying the public members of c. See ConfigState for options documentation. @@ -295,12 +295,12 @@ func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) // NewDefaultConfig returns a ConfigState with the following default settings. // -// Indent: " " -// MaxDepth: 0 -// DisableMethods: false -// DisablePointerMethods: false -// ContinueOnMethod: false -// SortKeys: false +// Indent: " " +// MaxDepth: 0 +// DisableMethods: false +// DisablePointerMethods: false +// ContinueOnMethod: false +// SortKeys: false func NewDefaultConfig() *ConfigState { return &ConfigState{Indent: " "} } diff --git a/vendor/github.com/davecgh/go-spew/spew/doc.go b/vendor/github.com/stretchr/testify/internal/spew/doc.go similarity index 65% rename from vendor/github.com/davecgh/go-spew/spew/doc.go rename to vendor/github.com/stretchr/testify/internal/spew/doc.go index aacaac6f..722e9aa7 100644 --- a/vendor/github.com/davecgh/go-spew/spew/doc.go +++ b/vendor/github.com/stretchr/testify/internal/spew/doc.go @@ -21,35 +21,36 @@ debugging. A quick overview of the additional features spew provides over the built-in printing facilities for Go data types are as follows: - * Pointers are dereferenced and followed - * Circular data structures are detected and handled properly - * Custom Stringer/error interfaces are optionally invoked, including - on unexported types - * Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - * Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output (only when using - Dump style) + - Pointers are dereferenced and followed + - Circular data structures are detected and handled properly + - Custom Stringer/error interfaces are optionally invoked, including + on unexported types + - Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + - Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output (only when using + Dump style) There are two different approaches spew allows for dumping Go data structures: - * Dump style which prints with newlines, customizable indentation, - and additional debug information such as types and all pointer addresses - used to indirect to the final value - * A custom Formatter interface that integrates cleanly with the standard fmt - package and replaces %v, %+v, %#v, and %#+v to provide inline printing - similar to the default %v while providing the additional functionality - outlined above and passing unsupported format verbs such as %x and %q - along to fmt + - Dump style which prints with newlines, customizable indentation, + and additional debug information such as types and all pointer addresses + used to indirect to the final value + - A custom Formatter interface that integrates cleanly with the standard fmt + package and replaces %v, %+v, %#v, and %#+v to provide inline printing + similar to the default %v while providing the additional functionality + outlined above and passing unsupported format verbs such as %x and %q + along to fmt -Quick Start +# Quick Start This section demonstrates how to quickly get started with spew. See the sections below for further details on formatting and configuration options. To dump a variable with full newlines, indentation, type, and pointer information use Dump, Fdump, or Sdump: + spew.Dump(myVar1, myVar2, ...) spew.Fdump(someWriter, myVar1, myVar2, ...) str := spew.Sdump(myVar1, myVar2, ...) @@ -58,12 +59,13 @@ Alternatively, if you would prefer to use format strings with a compacted inline printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types and pointer addresses): + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) -Configuration Options +# Configuration Options Configuration of spew is handled by fields in the ConfigState type. For convenience, all of the top-level functions use a global state available @@ -74,51 +76,52 @@ equivalent to the top-level functions. This allows concurrent configuration options. See the ConfigState documentation for more details. The following configuration options are available: - * Indent - String to use for each indentation level for Dump functions. - It is a single space by default. A popular alternative is "\t". - - * MaxDepth - Maximum number of levels to descend into nested data structures. - There is no limit by default. - - * DisableMethods - Disables invocation of error and Stringer interface methods. - Method invocation is enabled by default. - - * DisablePointerMethods - Disables invocation of error and Stringer interface methods on types - which only accept pointer receivers from non-pointer variables. - Pointer method invocation is enabled by default. - - * DisablePointerAddresses - DisablePointerAddresses specifies whether to disable the printing of - pointer addresses. This is useful when diffing data structures in tests. - - * DisableCapacities - DisableCapacities specifies whether to disable the printing of - capacities for arrays, slices, maps and channels. This is useful when - diffing data structures in tests. - - * ContinueOnMethod - Enables recursion into types after invoking error and Stringer interface - methods. Recursion after method invocation is disabled by default. - - * SortKeys - Specifies map keys should be sorted before being printed. Use - this to have a more deterministic, diffable output. Note that - only native types (bool, int, uint, floats, uintptr and string) - and types which implement error or Stringer interfaces are - supported with other types sorted according to the - reflect.Value.String() output which guarantees display - stability. Natural map order is used by default. - - * SpewKeys - Specifies that, as a last resort attempt, map keys should be - spewed to strings and sorted by those strings. This is only - considered if SortKeys is true. - -Dump Usage + + - Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + + - MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + + - DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + + - DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. + Pointer method invocation is enabled by default. + + - DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + + - DisableCapacities + DisableCapacities specifies whether to disable the printing of + capacities for arrays, slices, maps and channels. This is useful when + diffing data structures in tests. + + - ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + + - SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are + supported with other types sorted according to the + reflect.Value.String() output which guarantees display + stability. Natural map order is used by default. + + - SpewKeys + Specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only + considered if SortKeys is true. + +# Dump Usage Simply call spew.Dump with a list of variables you want to dump: @@ -133,7 +136,7 @@ A third option is to call spew.Sdump to get the formatted output as a string: str := spew.Sdump(myVar1, myVar2, ...) -Sample Dump Output +# Sample Dump Output See the Dump example for details on the setup of the types and variables being shown here. @@ -150,13 +153,14 @@ shown here. Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C command as shown. + ([]uint8) (len=32 cap=32) { 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| 00000020 31 32 |12| } -Custom Formatter +# Custom Formatter Spew provides a custom formatter that implements the fmt.Formatter interface so that it integrates cleanly with standard fmt package printing functions. The @@ -170,7 +174,7 @@ standard fmt package for formatting. In addition, the custom formatter ignores the width and precision arguments (however they will still work on the format specifiers not handled by the custom formatter). -Custom Formatter Usage +# Custom Formatter Usage The simplest way to make use of the spew custom formatter is to call one of the convenience functions such as spew.Printf, spew.Println, or spew.Printf. The @@ -184,15 +188,17 @@ functions have syntax you are most likely already familiar with: See the Index for the full list convenience functions. -Sample Formatter Output +# Sample Formatter Output Double pointer to a uint8: + %v: <**>5 %+v: <**>(0xf8400420d0->0xf8400420c8)5 %#v: (**uint8)5 %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 Pointer to circular struct with a uint8 field and a pointer to itself: + %v: <*>{1 <*>} %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} @@ -201,7 +207,7 @@ Pointer to circular struct with a uint8 field and a pointer to itself: See the Printf example for details on the setup of variables being shown here. -Errors +# Errors Since it is possible for custom Stringer/error interfaces to panic, spew detects them and handles them internally by printing the panic information diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/stretchr/testify/internal/spew/dump.go similarity index 96% rename from vendor/github.com/davecgh/go-spew/spew/dump.go rename to vendor/github.com/stretchr/testify/internal/spew/dump.go index f78d89fc..8323041a 100644 --- a/vendor/github.com/davecgh/go-spew/spew/dump.go +++ b/vendor/github.com/stretchr/testify/internal/spew/dump.go @@ -488,15 +488,15 @@ pointer addresses used to indirect to the final value. It provides the following features over the built-in printing facilities provided by the fmt package: - * Pointers are dereferenced and followed - * Circular data structures are detected and handled properly - * Custom Stringer/error interfaces are optionally invoked, including - on unexported types - * Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - * Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output + - Pointers are dereferenced and followed + - Circular data structures are detected and handled properly + - Custom Stringer/error interfaces are optionally invoked, including + on unexported types + - Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + - Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output The configuration options are controlled by an exported package global, spew.Config. See ConfigState for options documentation. diff --git a/vendor/github.com/davecgh/go-spew/spew/format.go b/vendor/github.com/stretchr/testify/internal/spew/format.go similarity index 100% rename from vendor/github.com/davecgh/go-spew/spew/format.go rename to vendor/github.com/stretchr/testify/internal/spew/format.go diff --git a/vendor/github.com/davecgh/go-spew/spew/spew.go b/vendor/github.com/stretchr/testify/internal/spew/spew.go similarity index 100% rename from vendor/github.com/davecgh/go-spew/spew/spew.go rename to vendor/github.com/stretchr/testify/internal/spew/spew.go diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go index efc89def..7f4d28d5 100644 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -11,11 +11,11 @@ import ( "sync" "time" - "github.com/davecgh/go-spew/spew" - "github.com/pmezard/go-difflib/difflib" "github.com/stretchr/objx" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/internal/difflib" + "github.com/stretchr/testify/internal/spew" ) // regex for GCCGO functions @@ -470,7 +470,7 @@ func callString(method string, arguments Arguments, includeArgumentValues bool) // Called tells the mock object that a method has been called, and gets an array // of arguments to return. Panics if the call is unexpected (i.e. not preceded by -// appropriate .On .Return() calls) +// appropriate [Mock.On] calls) // If Call.WaitFor is set, blocks until the channel is closed or receives a message. func (m *Mock) Called(arguments ...interface{}) Arguments { // get the calling function's name @@ -493,7 +493,7 @@ func (m *Mock) Called(arguments ...interface{}) Arguments { // MethodCalled tells the mock object that the given method has been called, and gets // an array of arguments to return. Panics if the call is unexpected (i.e. not preceded -// by appropriate .On .Return() calls) +// by appropriate [Mock.On] calls) // If Call.WaitFor is set, blocks until the channel is closed or receives a message. func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Arguments { m.mutex.Lock() @@ -510,8 +510,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen // as the return arguments. This is because: // // a) this is a totally unexpected call to this method, - // b) the arguments are not what was expected, or - // c) the developer has forgotten to add an accompanying On...Return pair. + // b) the arguments are not what was expected closestCall, mismatch := m.findClosestCall(methodName, arguments...) m.mutex.Unlock() @@ -595,7 +594,7 @@ type assertExpectationiser interface { AssertExpectations(TestingT) bool } -// AssertExpectationsForObjects asserts that everything specified with On and Return +// AssertExpectationsForObjects asserts that everything specified with [Mock.On] // of the specified objects was in fact called as expected. // // Calls may have occurred in any order. @@ -604,11 +603,12 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { h.Helper() } for _, obj := range testObjects { - if m, ok := obj.(*Mock); ok { - t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)") - obj = m + m, ok := obj.(assertExpectationiser) + if !ok { + t.Errorf("Invalid test object type %T. Expected reference to a mock.Mock, eg: 'AssertExpectationsForObjects(t, myMock)' or 'AssertExpectationsForObjects(t, &myMock.Mock)'", obj) + continue + } - m := obj.(assertExpectationiser) if !m.AssertExpectations(t) { t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m)) return false @@ -617,7 +617,7 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { return true } -// AssertExpectations asserts that everything specified with On and Return was +// AssertExpectations asserts that everything specified with [Mock.On] was // in fact called as expected. Calls may have occurred in any order. func (m *Mock) AssertExpectations(t TestingT) bool { if s, ok := t.(interface{ Skipped() bool }); ok && s.Skipped() { @@ -712,8 +712,8 @@ func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...inter return true } -// IsMethodCallable checking that the method can be called -// If the method was called more than `Repeatability` return false +// IsMethodCallable returns true if given methodName and arguments have an +// unsatisfied expected call registered in the Mock. func (m *Mock) IsMethodCallable(t TestingT, methodName string, arguments ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -833,6 +833,10 @@ type IsTypeArgument struct { // For example: // // args.Assert(t, IsType(""), IsType(0)) +// +// Mock cannot match interface types because the contained type will be passed +// to both IsType and Mock.Called, for the zero value of all interfaces this +// will be type. func IsType(t interface{}) *IsTypeArgument { return &IsTypeArgument{t: reflect.TypeOf(t)} } @@ -1012,7 +1016,7 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { actualT := reflect.TypeOf(actual) if actualT != expected.t { differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt) + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, safeTypeName(expected.t), safeTypeName(actualT), actualFmt) } case *FunctionalOptionsArgument: var name string @@ -1141,6 +1145,15 @@ func (args Arguments) Bool(index int) bool { return s } +// safeTypeName returns the reflect.Type's name without causing a panic. +// If the provided reflect.Type is nil, it returns the placeholder string "" +func safeTypeName(t reflect.Type) string { + if t == nil { + return "" + } + return t.Name() +} + func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { t := reflect.TypeOf(v) k := t.Kind() diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go index c8e3f94a..ee84cc47 100644 --- a/vendor/github.com/stretchr/testify/require/doc.go +++ b/vendor/github.com/stretchr/testify/require/doc.go @@ -1,4 +1,4 @@ -// Package require implements the same assertions as the `assert` package but +// Package require implements the same assertions as the assert package but // stops test execution when a test fails. // // # Example Usage @@ -21,8 +21,8 @@ // // # Assertions // -// The `require` package have same global functions as in the `assert` package, -// but instead of returning a boolean result they call `t.FailNow()`. +// The require package have same global functions as in the assert package, +// but instead of returning a boolean result they call [testing.T.FailNow]. // A consequence of this is that it must be called from the goroutine running // the test function, not from other goroutines created during the test. // diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 2d02f9bc..652871f2 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -180,7 +180,7 @@ func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...i t.FailNow() } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -195,7 +195,7 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte t.FailNow() } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -295,7 +295,7 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar t.FailNow() } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // require.Error(t, err) @@ -333,8 +333,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int t.FailNow() } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // require.ErrorContains(t, err, expectedErrorSubString) @@ -348,8 +348,8 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in t.FailNow() } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // require.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") @@ -387,7 +387,7 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface t.FailNow() } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // require.Errorf(t, err, "error message %s", "formatted") @@ -429,7 +429,7 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // time.Sleep(8*time.Second) // externalValue = true // }() -// require.EventuallyWithT(t, func(c *require.CollectT) { +// require.EventuallyWithT(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // require.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") @@ -457,10 +457,10 @@ func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitF // time.Sleep(8*time.Second) // externalValue = true // }() -// require.EventuallyWithTf(t, func(c *require.CollectT, "error message %s", "formatted") { +// require.EventuallyWithTf(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // require.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") 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() @@ -671,8 +671,6 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // body that contains a string. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -687,8 +685,6 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // body that contains a string. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -703,8 +699,6 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // body that does not contain a string. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -719,8 +713,6 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // body that does not contain a string. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -734,8 +726,6 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPError asserts that a specified handler returns an error status code. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -749,8 +739,6 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPErrorf asserts that a specified handler returns an error status code. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -764,8 +752,6 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirect asserts that a specified handler returns a redirect status code. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -779,8 +765,6 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin // HTTPRedirectf asserts that a specified handler returns a redirect status code. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -794,8 +778,6 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCode asserts that a specified handler returns a specified status code. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -809,8 +791,6 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url str // HTTPStatusCodef asserts that a specified handler returns a specified status code. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -824,8 +804,6 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccess asserts that a specified handler returns a success status code. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -839,8 +817,6 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string // HTTPSuccessf asserts that a specified handler returns a success status code. // // require.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{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1385,12 +1361,11 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { t.FailNow() } -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // -// actualObj, err := SomeFunction() -// if require.NoError(t, err) { -// require.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// require.NoError(t, err) +// require.Equal(t, expectedObj, actualObj) func NoError(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1401,12 +1376,11 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) { t.FailNow() } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // -// actualObj, err := SomeFunction() -// if require.NoErrorf(t, err, "error message %s", "formatted") { -// require.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// require.NoErrorf(t, err, "error message %s", "formatted") +// require.Equal(t, expectedObj, actualObj) func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1515,9 +1489,8 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str // NotEmpty asserts that the specified object is NOT [Empty]. // -// if require.NotEmpty(t, obj) { -// require.Equal(t, "two", obj[1]) -// } +// require.NotEmpty(t, obj) +// require.Equal(t, "two", obj[1]) func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1530,9 +1503,8 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT [Empty]. // -// if require.NotEmptyf(t, obj, "error message %s", "formatted") { -// require.Equal(t, "two", obj[1]) -// } +// require.NotEmptyf(t, obj, "error message %s", "formatted") +// require.Equal(t, "two", obj[1]) func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2135,7 +2107,19 @@ func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, t.FailNow() } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// require.YAMLEq(t, expected, actual) func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2146,7 +2130,19 @@ func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ t.FailNow() } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// require.YAMLEqf(t, expected, actual, "error message %s", "formatted") func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require.go.tmpl b/vendor/github.com/stretchr/testify/require/require.go.tmpl index 8b328368..6a975501 100644 --- a/vendor/github.com/stretchr/testify/require/require.go.tmpl +++ b/vendor/github.com/stretchr/testify/require/require.go.tmpl @@ -1,4 +1,4 @@ -{{ replace .Comment "assert." "require."}} +{{.CommentRequire}} func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index e6f7e944..edac147e 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -147,7 +147,7 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs Equal(a.t, expected, actual, msgAndArgs...) } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -159,7 +159,7 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... EqualError(a.t, theError, errString, msgAndArgs...) } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -241,7 +241,7 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string Equalf(a.t, expected, actual, msg, args...) } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // a.Error(err) @@ -270,8 +270,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. ErrorAsf(a.t, err, target, msg, args...) } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContains(err, expectedErrorSubString) @@ -282,8 +282,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . ErrorContains(a.t, theError, contains, msgAndArgs...) } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") @@ -312,7 +312,7 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter ErrorIsf(a.t, err, target, msg, args...) } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // a.Errorf(err, "error message %s", "formatted") @@ -373,10 +373,10 @@ func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), w // time.Sleep(8*time.Second) // externalValue = true // }() -// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// a.EventuallyWithTf(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") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") 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() @@ -539,8 +539,6 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // body that contains a string. // // 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -552,8 +550,6 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // body that contains a string. // // 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -565,8 +561,6 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // body that does not contain a string. // // 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -578,8 +572,6 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // body that does not contain a string. // // 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -590,8 +582,6 @@ 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"}} -// -// 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -602,8 +592,6 @@ 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"}} -// -// 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -614,8 +602,6 @@ 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"}} -// -// 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -626,8 +612,6 @@ 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"}} -// -// 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -638,8 +622,6 @@ 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) -// -// 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -650,8 +632,6 @@ 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") -// -// 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -662,8 +642,6 @@ 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) -// -// 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -674,8 +652,6 @@ 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") -// -// 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{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1097,12 +1073,11 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) NoDirExistsf(a.t, path, msg, args...) } -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// a.NoError(err) +// a.Equal(expectedObj, actualObj) func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1110,12 +1085,11 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { NoError(a.t, err, msgAndArgs...) } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// a.NoErrorf(err, "error message %s", "formatted") +// a.Equal(expectedObj, actualObj) func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1203,9 +1177,8 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg // NotEmpty asserts that the specified object is NOT [Empty]. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// a.NotEmpty(obj) +// a.Equal("two", obj[1]) func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1215,9 +1188,8 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT [Empty]. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// a.NotEmptyf(obj, "error message %s", "formatted") +// a.Equal("two", obj[1]) func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1691,7 +1663,19 @@ func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Ti WithinRangef(a.t, actual, start, end, msg, args...) } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEq(expected, actual) func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1699,7 +1683,19 @@ func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interf YAMLEq(a.t, expected, actual, msgAndArgs...) } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEqf(expected, actual, "error message %s", "formatted") func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl index 54124df1..b3b751de 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl +++ b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl @@ -1,4 +1,4 @@ -{{.CommentWithoutT "a"}} +{{.CommentRequireWithoutT "a"}} func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { if h, ok := a.t.(tHelper); ok { h.Helper() } {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) diff --git a/vendor/github.com/stretchr/testify/require/requirements.go b/vendor/github.com/stretchr/testify/require/requirements.go index 6b7ce929..375adb0a 100644 --- a/vendor/github.com/stretchr/testify/require/requirements.go +++ b/vendor/github.com/stretchr/testify/require/requirements.go @@ -12,18 +12,18 @@ type tHelper = interface { // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful // for table driven tests. -type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) +type ComparisonAssertionFunc = func(TestingT, interface{}, interface{}, ...interface{}) // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful // for table driven tests. -type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) +type ValueAssertionFunc = func(TestingT, interface{}, ...interface{}) // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful // for table driven tests. -type BoolAssertionFunc func(TestingT, bool, ...interface{}) +type BoolAssertionFunc = func(TestingT, bool, ...interface{}) // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful // for table driven tests. -type ErrorAssertionFunc func(TestingT, error, ...interface{}) +type ErrorAssertionFunc = func(TestingT, error, ...interface{}) //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" diff --git a/vendor/github.com/tetafro/godot/.golangci.yml b/vendor/github.com/tetafro/godot/.golangci.yml index 2b626186..9679efa7 100644 --- a/vendor/github.com/tetafro/godot/.golangci.yml +++ b/vendor/github.com/tetafro/godot/.golangci.yml @@ -1,9 +1,8 @@ +version: "2" run: concurrency: 2 - timeout: 5m - linters: - disable-all: true + default: none enable: - asciicheck - bodyclose @@ -22,12 +21,8 @@ linters: - gocritic - gocyclo - godot - - gofmt - - gofumpt - - goimports - goprintffuncname - gosec - - gosimple - govet - importas - ineffassign @@ -41,38 +36,39 @@ linters: - revive - rowserrcheck - sqlclosecheck - - sqlclosecheck - staticcheck - - stylecheck - - typecheck - unconvert - unparam - unused - wastedassign - whitespace - wrapcheck - -linters-settings: - godot: - scope: toplevel - -issues: - exclude-use-default: false - exclude: - - "do not define dynamic errors, use wrapped static errors instead" - exclude-files: - - ./testdata/ - exclude-rules: - - path: _test\.go - linters: - - dupl - - errcheck - - funlen - - gocognit - - cyclop - - gosec - - noctx - - path: main\.go - linters: - - cyclop - - gomnd + exclusions: + rules: + - path: _test\.go + linters: + - cyclop + - dupl + - errcheck + - gocognit + - goconst + - gocyclo + - gosec + - noctx + - path: main\.go + linters: + - cyclop + - gocognit + - path: main\.go + text: '`defer cancel\(\)` will not run' + - path: (.+)\.go$ + text: 'G404: Use of weak random number generator' + - path: (.+)\.go$ + text: do not define dynamic errors, use wrapped static errors instead + - path: (.+)\.go$ + text: Error return value of `.*.Body.Close` is not checked +formatters: + enable: + - gofmt + - gofumpt + - goimports diff --git a/vendor/github.com/tetafro/godot/.goreleaser.yml b/vendor/github.com/tetafro/godot/.goreleaser.yml index 2f0c2466..ff599202 100644 --- a/vendor/github.com/tetafro/godot/.goreleaser.yml +++ b/vendor/github.com/tetafro/godot/.goreleaser.yml @@ -5,7 +5,7 @@ builds: checksum: name_template: checksums.txt snapshot: - name_template: "{{ .Tag }}" + version_template: "{{ .Tag }}" changelog: sort: asc filters: diff --git a/vendor/github.com/tetafro/godot/checks.go b/vendor/github.com/tetafro/godot/checks.go index 0301fa93..13e867fb 100644 --- a/vendor/github.com/tetafro/godot/checks.go +++ b/vendor/github.com/tetafro/godot/checks.go @@ -111,7 +111,9 @@ func checkPeriod(c comment) *Issue { // Get the offset of the first symbol in the last line of the comment. // This value is used only in golangci-lint to point to the problem, // and to replace the line when running in auto-fix mode. - offset := c.start.Offset + // For inline comments, the line starts before the comment, so we + // subtract the column offset to get the line start. + offset := c.start.Offset - (c.start.Column - 1) for i := 0; i < pos.line-1; i++ { offset += len(c.lines[i]) + 1 } @@ -218,7 +220,9 @@ func checkCapital(c comment) []Issue { // Get the offset of the first symbol in the current issue's line. // This value is used only in golangci-lint to point to the problem, // and to replace the line when running in auto-fix mode. - offset := c.start.Offset + // For inline comments, the line starts before the comment, so we + // subtract the column offset to get the line start. + offset := c.start.Offset - (c.start.Column - 1) for i := 0; i < pos.line-1; i++ { offset += len(c.lines[i]) + 1 } diff --git a/vendor/github.com/tetafro/godot/file.go b/vendor/github.com/tetafro/godot/file.go index 19b0ebe9..7b878c29 100644 --- a/vendor/github.com/tetafro/godot/file.go +++ b/vendor/github.com/tetafro/godot/file.go @@ -87,6 +87,8 @@ func (pf *parsedFile) getComments(scope Scope, exclude []*regexp.Regexp) []comme // getBlockComments gets comments from the inside of top level blocks: // var (...), const (...). +// +//nolint:cyclop func (pf *parsedFile) getBlockComments(exclude []*regexp.Regexp) []comment { var comments []comment for _, decl := range pf.file.Decls { @@ -109,12 +111,14 @@ func (pf *parsedFile) getBlockComments(exclude []*regexp.Regexp) []comment { // Skip comments that are not top-level for this block // (the block itself is top level, so comments inside this block // would be on column 2) - //nolint:gomnd if pf.fset.Position(c.Pos()).Column != 2 { continue } firstLine := pf.fset.Position(c.Pos()).Line lastLine := pf.fset.Position(c.End()).Line + if firstLine < 1 || lastLine < firstLine || lastLine > len(pf.lines) { + continue // broken consistency, probably by the `//line` directive + } comments = append(comments, comment{ lines: pf.lines[firstLine-1 : lastLine], text: getText(c, exclude), @@ -127,7 +131,7 @@ func (pf *parsedFile) getBlockComments(exclude []*regexp.Regexp) []comment { // getTopLevelComments gets all top level comments. func (pf *parsedFile) getTopLevelComments(exclude []*regexp.Regexp) []comment { - var comments []comment //nolint:prealloc + var comments []comment for _, c := range pf.file.Comments { if c == nil || len(c.List) == 0 { continue @@ -137,6 +141,9 @@ func (pf *parsedFile) getTopLevelComments(exclude []*regexp.Regexp) []comment { } firstLine := pf.fset.Position(c.Pos()).Line lastLine := pf.fset.Position(c.End()).Line + if firstLine < 1 || lastLine < firstLine || lastLine > len(pf.lines) { + continue // broken consistency, probably by the `//line` directive + } comments = append(comments, comment{ lines: pf.lines[firstLine-1 : lastLine], text: getText(c, exclude), @@ -148,7 +155,7 @@ func (pf *parsedFile) getTopLevelComments(exclude []*regexp.Regexp) []comment { // getDeclarationComments gets top level declaration comments. func (pf *parsedFile) getDeclarationComments(exclude []*regexp.Regexp) []comment { - var comments []comment //nolint:prealloc + var comments []comment for _, decl := range pf.file.Decls { var cg *ast.CommentGroup switch d := decl.(type) { @@ -164,6 +171,9 @@ func (pf *parsedFile) getDeclarationComments(exclude []*regexp.Regexp) []comment firstLine := pf.fset.Position(cg.Pos()).Line lastLine := pf.fset.Position(cg.End()).Line + if firstLine < 1 || lastLine < firstLine || lastLine > len(pf.lines) { + continue // broken consistency, probably by the `//line` directive + } comments = append(comments, comment{ lines: pf.lines[firstLine-1 : lastLine], text: getText(cg, exclude), @@ -175,13 +185,16 @@ func (pf *parsedFile) getDeclarationComments(exclude []*regexp.Regexp) []comment // getNoInlineComments gets all except inline comments. func (pf *parsedFile) getNoInlineComments(exclude []*regexp.Regexp) []comment { - var comments []comment //nolint:prealloc + var comments []comment for _, c := range pf.file.Comments { if c == nil || len(c.List) == 0 { continue } firstLine := pf.fset.Position(c.Pos()).Line lastLine := pf.fset.Position(c.End()).Line + if firstLine < 1 || lastLine < firstLine || lastLine > len(pf.lines) { + continue // broken consistency, probably by the `//line` directive + } c := comment{ lines: pf.lines[firstLine-1 : lastLine], @@ -203,13 +216,16 @@ func (pf *parsedFile) getNoInlineComments(exclude []*regexp.Regexp) []comment { // getAllComments gets every single comment from the file. func (pf *parsedFile) getAllComments(exclude []*regexp.Regexp) []comment { - var comments []comment //nolint:prealloc + var comments []comment for _, c := range pf.file.Comments { if c == nil || len(c.List) == 0 { continue } firstLine := pf.fset.Position(c.Pos()).Line lastLine := pf.fset.Position(c.End()).Line + if firstLine < 1 || lastLine < firstLine || lastLine > len(pf.lines) { + continue // broken consistency, probably by the `//line` directive + } comments = append(comments, comment{ lines: pf.lines[firstLine-1 : lastLine], start: pf.fset.Position(c.List[0].Slash), diff --git a/vendor/github.com/timakin/bodyclose/passes/bodyclose/bodyclose.go b/vendor/github.com/timakin/bodyclose/passes/bodyclose/bodyclose.go index ae860d72..4a862521 100644 --- a/vendor/github.com/timakin/bodyclose/passes/bodyclose/bodyclose.go +++ b/vendor/github.com/timakin/bodyclose/passes/bodyclose/bodyclose.go @@ -16,12 +16,18 @@ import ( var Analyzer = &analysis.Analyzer{ Name: "bodyclose", Doc: Doc, - Run: new(runner).run, + Run: run, Requires: []*analysis.Analyzer{ buildssa.Analyzer, }, } +func init() { + Analyzer.Flags.BoolVar(&checkConsumptionFlag, "check-consumption", false, "also check that response body is consumed") +} + +var checkConsumptionFlag bool + const ( Doc = "checks whether HTTP response body is closed successfully" @@ -30,18 +36,21 @@ const ( ) type runner struct { - pass *analysis.Pass - resObj types.Object - resTyp *types.Pointer - bodyObj types.Object - closeMthd *types.Func - skipFile map[*ast.File]bool + pass *analysis.Pass + resObj types.Object + resTyp *types.Pointer + bodyObj types.Object + closeMthd *types.Func + skipFile map[*ast.File]bool + checkConsumption bool } -// run executes an analysis for the pass. The receiver is passed -// by value because this func is called in parallel for different passes. -func (r runner) run(pass *analysis.Pass) (interface{}, error) { - r.pass = pass +// run executes an analysis for the pass +func run(pass *analysis.Pass) (interface{}, error) { + r := runner{ + pass: pass, + checkConsumption: checkConsumptionFlag, + } funcs := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs r.resObj = analysisutil.LookupFromImports(pass.Pkg.Imports(), nethttpPath, "Response") @@ -96,7 +105,11 @@ FuncLoop: for i := range b.Instrs { pos := b.Instrs[i].Pos() if r.isopen(b, i) { - pass.Reportf(pos, "response body must be closed") + if r.checkConsumption { + pass.Reportf(pos, "response body must be closed and consumed") + } else { + pass.Reportf(pos, "response body must be closed") + } } } } @@ -216,11 +229,8 @@ func (r *runner) isopen(b *ssa.BasicBlock, i int) bool { if len(*bOp.Referrers()) == 0 { return true } - ccalls := *bOp.Referrers() - for _, ccall := range ccalls { - if r.isCloseCall(ccall) { - return false - } + if r.isBodyProperlyHandled(bOp) { + return false } } case *ssa.Phi: // Called in the higher-level block @@ -242,11 +252,8 @@ func (r *runner) isopen(b *ssa.BasicBlock, i int) bool { if len(*bOp.Referrers()) == 0 { return true } - ccalls := *bOp.Referrers() - for _, ccall := range ccalls { - if r.isCloseCall(ccall) { - return false - } + if r.isBodyProperlyHandled(bOp) { + return false } } } @@ -268,6 +275,7 @@ func (r *runner) getReqCall(instr ssa.Instruction) (*ssa.Call, bool) { strings.Contains(callType, "net/http.ResponseController") { return nil, false } + return call, true } @@ -300,6 +308,102 @@ func (r *runner) getBodyOp(instr ssa.Instruction) (*ssa.UnOp, bool) { return op, true } +// isBodyProperlyHandled checks if response body is properly handled (closed and optionally consumed based on flag) +func (r *runner) isBodyProperlyHandled(bOp *ssa.UnOp) bool { + ccalls := *bOp.Referrers() + + for _, ccall := range ccalls { + if r.isCloseCall(ccall) { + // Early return if consumption checking is disabled + if !r.checkConsumption { + return true + } + // Close found and consumption checking enabled - check consumption + return r.hasConsumptionForBody(bOp) + } + } + + // No close call found + return false +} + +// hasConsumptionForBody searches the function for consumption calls that use the specific response body +func (r *runner) hasConsumptionForBody(bodyOp *ssa.UnOp) bool { + fn := bodyOp.Block().Parent() + + // Search for consumption functions that specifically consume this response body + for _, block := range fn.Blocks { + for _, blockInstr := range block.Instrs { + if call, ok := blockInstr.(*ssa.Call); ok { + if r.isConsumptionFunction(call) && r.isCallUsingBody(call, bodyOp) { + return true + } + } + } + } + + return false +} + +// isCallUsingBody checks if a consumption function call uses the specific response body +func (r *runner) isCallUsingBody(call *ssa.Call, responseBodyOp *ssa.UnOp) bool { + // Get the FieldAddr of the response body we're checking + responseBodyFieldAddr, ok := responseBodyOp.X.(*ssa.FieldAddr) + if !ok { + return false + } + + // Check if any argument to the call refers to this specific response body + for _, arg := range call.Call.Args { + if r.isArgumentMatchingBody(arg, responseBodyFieldAddr) { + return true + } + } + + return false +} + +// isArgumentMatchingBody checks if a function argument refers to the same response body instance +func (r *runner) isArgumentMatchingBody(arg ssa.Value, responseBodyFieldAddr *ssa.FieldAddr) bool { + switch v := arg.(type) { + case *ssa.FieldAddr: + // Direct field access - check if it's accessing Body field of same response + return v.X == responseBodyFieldAddr.X && v.Field == responseBodyFieldAddr.Field + case *ssa.UnOp: + // Dereference of field access - check if it's dereferencing the same response body field + if fieldAddr, ok := v.X.(*ssa.FieldAddr); ok { + return fieldAddr.X == responseBodyFieldAddr.X && fieldAddr.Field == responseBodyFieldAddr.Field + } + case *ssa.ChangeInterface: + // Type conversion - check if it converts the response body + if unOp, ok := v.X.(*ssa.UnOp); ok { + if fieldAddr, ok := unOp.X.(*ssa.FieldAddr); ok { + return fieldAddr.X == responseBodyFieldAddr.X && fieldAddr.Field == responseBodyFieldAddr.Field + } + } + } + return false +} + +func (r *runner) isConsumptionFunction(call *ssa.Call) bool { + if call.Call.StaticCallee() != nil { + callee := call.Call.StaticCallee() + if callee.Pkg != nil { + pkg := callee.Pkg.Pkg.Path() + name := callee.Name() + + // Check for known consumption functions + if (pkg == "io" && (name == "Copy" || name == "ReadAll")) || + (pkg == "io/ioutil" && name == "ReadAll") || + (pkg == "encoding/json" && name == "NewDecoder") || + (pkg == "bufio" && (name == "NewScanner" || name == "NewReader")) { + return true + } + } + } + return false +} + func (r *runner) isCloseCall(ccall ssa.Instruction) bool { switch ccall := ccall.(type) { case *ssa.Defer: diff --git a/vendor/github.com/uudashr/iface/identical/identical.go b/vendor/github.com/uudashr/iface/identical/identical.go index 0f470e54..4db64238 100644 --- a/vendor/github.com/uudashr/iface/identical/identical.go +++ b/vendor/github.com/uudashr/iface/identical/identical.go @@ -5,7 +5,7 @@ import ( "go/ast" "go/token" "go/types" - "reflect" + "os" "slices" "strings" @@ -15,7 +15,7 @@ import ( "golang.org/x/tools/go/ast/inspector" ) -// Analyzer is the analysis pass for detecting identical interfaces. +// Analyzer detects interfaces within the same package that have identical methods or type constraints. var Analyzer = newAnalyzer() func newAnalyzer() *analysis.Analyzer { @@ -56,46 +56,49 @@ func (r *runner) run(pass *analysis.Pass) (interface{}, error) { } if r.debug { - fmt.Printf("GenDecl: %v specs=%d\n", decl.Tok, len(decl.Specs)) + fmt.Fprintf(os.Stderr, "GenDecl: %v specs=%d\n", decl.Tok, len(decl.Specs)) } if decl.Tok != token.TYPE { return } + if directive.ShouldIgnore(decl.Doc, pass.Analyzer.Name) { + return + } + for i, spec := range decl.Specs { - if r.debug { - fmt.Printf(" spec[%d]: %v %v\n", i, spec, reflect.TypeOf(spec)) - } + r.debugf(" spec[%d]: %v %T\n", i, spec, spec) ts, ok := spec.(*ast.TypeSpec) if !ok { - return + // this code is unreachable since we already have guard the token type + continue } + r.debugf(" -> ts.Type %T\n", ts.Type) + ifaceType, ok := ts.Type.(*ast.InterfaceType) if !ok { - return + continue } if r.debug { - fmt.Println("Interface declaration:", ts.Name.Name, ts.Pos(), len(ifaceType.Methods.List)) + fmt.Fprintln(os.Stderr, " -> Interface declaration:", ts.Name.Name, ts.Pos(), len(ifaceType.Methods.List)) - for i, field := range ifaceType.Methods.List { + for j, field := range ifaceType.Methods.List { switch ft := field.Type.(type) { case *ast.FuncType: - fmt.Printf(" [%d] Field: func %s %v %v\n", i, field.Names[0].Name, reflect.TypeOf(field.Type), field.Pos()) + fmt.Fprintf(os.Stderr, " [%d] Field: func %s %T %v\n", j, field.Names[0].Name, ft, field.Pos()) case *ast.Ident: - fmt.Printf(" [%d] Field: iface %s %v %v\n", i, ft.Name, reflect.TypeOf(field.Type), field.Pos()) + fmt.Fprintf(os.Stderr, " [%d] Field: iface %s %T %v\n", j, ft.Name, ft, field.Pos()) default: - fmt.Printf(" [%d] Field: unknown %v\n", i, reflect.TypeOf(ft)) + fmt.Fprintf(os.Stderr, " [%d] Field: unknown %T\n", j, ft) } } } - dir := directive.ParseIgnore(decl.Doc) - if dir != nil && dir.ShouldIgnore(pass.Analyzer.Name) { - // skip due to ignore directive + if directive.ShouldIgnore(ts.Doc, pass.Analyzer.Name) { continue } @@ -103,12 +106,12 @@ func (r *runner) run(pass *analysis.Pass) (interface{}, error) { obj := pass.TypesInfo.Defs[ts.Name] if obj == nil { - return + continue } iface, ok := obj.Type().Underlying().(*types.Interface) if !ok { - return + continue } ifaceTypes[ts.Name.Name] = iface @@ -144,6 +147,12 @@ func (r *runner) run(pass *analysis.Pass) (interface{}, error) { func (r *runner) debugln(a ...any) { if r.debug { - fmt.Println(a...) + fmt.Fprintln(os.Stderr, a...) + } +} + +func (r *runner) debugf(format string, a ...any) { + if r.debug { + fmt.Fprintf(os.Stderr, format, a...) } } diff --git a/vendor/github.com/uudashr/iface/internal/directive/directive.go b/vendor/github.com/uudashr/iface/internal/directive/directive.go index f7c3ee34..8589f804 100644 --- a/vendor/github.com/uudashr/iface/internal/directive/directive.go +++ b/vendor/github.com/uudashr/iface/internal/directive/directive.go @@ -74,3 +74,12 @@ func (i *Ignore) ShouldIgnore(name string) bool { return i.hasName(name) } + +func ShouldIgnore(doc *ast.CommentGroup, name string) bool { + dir := ParseIgnore(doc) + if dir == nil { + return false + } + + return dir.ShouldIgnore(name) +} diff --git a/vendor/github.com/uudashr/iface/opaque/opaque.go b/vendor/github.com/uudashr/iface/opaque/opaque.go index c3cba80a..f3431ca0 100644 --- a/vendor/github.com/uudashr/iface/opaque/opaque.go +++ b/vendor/github.com/uudashr/iface/opaque/opaque.go @@ -3,8 +3,9 @@ package opaque import ( "fmt" "go/ast" + "go/token" "go/types" - "reflect" + "os" "strings" "github.com/uudashr/iface/internal/directive" @@ -13,7 +14,7 @@ import ( "golang.org/x/tools/go/ast/inspector" ) -// Analyzer is the analysis pass for detecting opaque interface returns. +// Analyzer detects functions that return an interface type, but only ever return a single concrete implementation. var Analyzer = newAnalyzer() func newAnalyzer() *analysis.Analyzer { @@ -49,7 +50,10 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { funcDecl := n.(*ast.FuncDecl) if funcDecl.Recv != nil { - // skip methods + // Skip methods because their return types may be dictated by interface + // contracts that the receiver type satisfies. Unlike standalone functions, + // methods are often created to fulfill an interface, so the return type + // is not a free design choice. Flagging these would produce false positives. return } @@ -64,44 +68,56 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { } if r.debug { - fmt.Printf("Function declaration %s\n", funcDecl.Name.Name) - fmt.Printf(" Results len=%d\n", len(funcDecl.Type.Results.List)) + fmt.Fprintf(os.Stderr, "Function declaration %s\n", funcDecl.Name.Name) + fmt.Fprintf(os.Stderr, " Results len=%d\n", len(funcDecl.Type.Results.List)) } - dir := directive.ParseIgnore(funcDecl.Doc) - if dir != nil && dir.ShouldIgnore(pass.Analyzer.Name) { - // skip ignored function + if directive.ShouldIgnore(funcDecl.Doc, pass.Analyzer.Name) { return } - // Pre-check, only function that has interface return type will be processed - var hasInterfaceReturnType bool + var ( + hasInterfaceReturnType bool + outCount int + ) - var outCount int + namedReturnObjs := make(map[*types.Var]int) + { + idx := 0 - for i, result := range funcDecl.Type.Results.List { - outInc := 1 - if namesLen := len(result.Names); namesLen > 0 { - outInc = namesLen - } + for i, result := range funcDecl.Type.Results.List { + if r.debug { + fmt.Fprintf(os.Stderr, " result[%d] %v %T names=%v\n", i, result.Type, result.Type, result.Names) + } - outCount += outInc + for j, name := range result.Names { + if obj := pass.TypesInfo.Defs[name]; obj != nil { + r.debugf(" name[%d] %v def=%v defAddr=%p\n", j, name, obj, obj) - resType := result.Type - typ := pass.TypesInfo.TypeOf(resType) + if v, ok := obj.(*types.Var); ok { + namedReturnObjs[v] = idx + } + } else { + r.debugf(" name[%d] %v def=unknown\n", j, name) + } - if r.debug { - fmt.Printf(" [%d] len=%d %v %v %v | %v %v interface=%t\n", i, len(result.Names), result.Names, resType, reflect.TypeOf(resType), typ, reflect.TypeOf(typ), types.IsInterface(typ)) - } + idx++ + } - if types.IsInterface(typ) && !hasInterfaceReturnType { - hasInterfaceReturnType = true + if len(result.Names) == 0 { + idx++ + } + + outCount += max(1, len(result.Names)) + + if !hasInterfaceReturnType { + typ := pass.TypesInfo.TypeOf(result.Type) + hasInterfaceReturnType = typ != nil && types.IsInterface(typ) + } } } - if r.debug { - fmt.Printf(" hasInterface=%t outCount=%d\n", hasInterfaceReturnType, outCount) - } + r.debugf(" hasInterface=%t outCount=%d\n", hasInterfaceReturnType, outCount) if !hasInterfaceReturnType { // skip, since it has no interface return type @@ -114,37 +130,108 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { retStmtTypes[i] = make(map[types.Type]struct{}) } + r.debugln(" Body") ast.Inspect(funcDecl.Body, func(n ast.Node) bool { switch n := n.(type) { case *ast.FuncLit: - // ignore nested functions + r.debugln(" FuncLit") + return false - case *ast.ReturnStmt: + case *ast.AssignStmt: + if n.Tok != token.ASSIGN { + return true + } + if r.debug { - fmt.Printf(" Return statements %v len=%d\n", n.Results, len(n.Results)) + r.debugf(" AssignStmt token %q lhsLen=%d rhsLen=%d\n", n.Tok, len(n.Lhs), len(n.Rhs)) } - for i, result := range n.Results { + for i, lhs := range n.Lhs { + r.debugf(" lhs[%d] %v %T\n", i, lhs, lhs) + + ident, ok := lhs.(*ast.Ident) + if !ok { + continue + } + + obj := pass.TypesInfo.Uses[ident] + r.debugf(" -> typeObj %v\n", obj) + + if obj == nil { + continue + } + + v, ok := obj.(*types.Var) + r.debugf(" -> types.Var=%t\n", ok) + + if !ok { + continue + } + + pos, found := namedReturnObjs[v] + r.debugf(" -> namedFound=%t\n", found) + + if !found { + continue + } + + var typ types.Type + if r.debug { - fmt.Printf(" [%d] %v %v\n", i, result, reflect.TypeOf(result)) + fmt.Fprintf(os.Stderr, " -> rhsLen=%d\n", len(n.Rhs)) + } + + switch { + case len(n.Rhs) == 1: + typ = pass.TypesInfo.TypeOf(n.Rhs[0]) + if tuple, ok := typ.(*types.Tuple); ok { + if i < tuple.Len() { + typ = tuple.At(i).Type() + } else { + typ = nil + } + } + + r.debugf(" -> rhs[0] typ %v %T\n", typ, typ) + case i < len(n.Rhs): + typ = pass.TypesInfo.TypeOf(n.Rhs[i]) + r.debugf(" -> rhs[%d] typ %v %T\n", i, typ, typ) + default: + typ = nil + + r.debugf(" -> rhs default\n") + } + + if typ != nil && !isUntypedNil(typ) { + retStmtTypes[pos][typ] = struct{}{} } + } + + return true + case *ast.ReturnStmt: + if r.debug { + fmt.Fprintf(os.Stderr, " ReturnStmt results %v len=%d\n", n.Results, len(n.Results)) + } + + for i, result := range n.Results { + r.debugf(" [%d] %v %T\n", i, result, result) switch res := result.(type) { case *ast.CallExpr: - if r.debug { - fmt.Printf(" CallExpr Fun: %v %v\n", res.Fun, reflect.TypeOf(res.Fun)) - } + // Multi-return calls produce a Tuple; unwrap each element to + // record types by position. Single-value calls fall to default. + r.debugf(" CallExpr Fun: %v %T\n", res.Fun, res.Fun) typ := pass.TypesInfo.TypeOf(res) switch typ := typ.(type) { case *types.Tuple: - for i := range typ.Len() { - v := typ.At(i) + for j := range typ.Len() { + v := typ.At(j) vTyp := v.Type() - retStmtTypes[i][vTyp] = struct{}{} + retStmtTypes[j][vTyp] = struct{}{} if r.debug { - fmt.Printf(" Tuple [%d]: %v %v | %v %v interface=%t\n", i, v, reflect.TypeOf(v), vTyp, reflect.TypeOf(vTyp), types.IsInterface(vTyp)) + fmt.Fprintf(os.Stderr, " Tuple [%d]: %v %T | %v %T interface=%t\n", j, v, v, vTyp, vTyp, types.IsInterface(vTyp)) } } default: @@ -152,36 +239,33 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { } case *ast.Ident: - if r.debug { - fmt.Printf(" Ident: %v %v\n", res, reflect.TypeOf(res)) + // Skip untyped nil — not a concrete type. All other identifiers + // (variables, constants, etc.) are recorded as-is. + r.debugf(" Ident: %v %T\n", res, res) + + if obj := pass.TypesInfo.Uses[res]; obj != nil { + if v, ok := obj.(*types.Var); ok { + if _, found := namedReturnObjs[v]; found { + break + } + } } typ := pass.TypesInfo.TypeOf(res) isNilStmt := isUntypedNil(typ) if r.debug { - fmt.Printf(" Ident type: %v %v interface=%t, untypedNil=%t\n", typ, reflect.TypeOf(typ), types.IsInterface(typ), isNilStmt) + fmt.Fprintf(os.Stderr, " Ident type: %v %T interface=%t, untypedNil=%t\n", typ, typ, types.IsInterface(typ), isNilStmt) } if !isNilStmt { retStmtTypes[i][typ] = struct{}{} } - case *ast.UnaryExpr: - if r.debug { - fmt.Printf(" UnaryExpr X: %v \n", res.X) - } - - typ := pass.TypesInfo.TypeOf(res) - - if r.debug { - fmt.Printf(" UnaryExpr type: %v %v interface=%t\n", typ, reflect.TypeOf(typ), types.IsInterface(typ)) - } - - retStmtTypes[i][typ] = struct{}{} default: - if r.debug { - fmt.Printf(" Unknown: %v %v\n", res, reflect.TypeOf(res)) - } + // Catches everything else: UnaryExpr (`&foo`), SelectorExpr, + // TypeAssertExpr, CompositeLit, etc. pass.TypesInfo.TypeOf(res) + // resolves the type correctly regardless of AST node kind. + r.debugf(" OtherExpr: %v %T\n", res, res) typ := pass.TypesInfo.TypeOf(res) retStmtTypes[i][typ] = struct{}{} @@ -215,8 +299,10 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { continue } - if typ.String() == "error" { - // very common case to have return type error + errorType := types.Universe.Lookup("error").Type() + anyType := types.Universe.Lookup("any").Type() + + if types.Identical(typ, errorType) || types.Identical(typ, anyType) { continue } @@ -251,7 +337,7 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { } if r.debug { - fmt.Printf("stmtType: %v %v | %v %v\n", stmtTyp, reflect.TypeOf(stmtTyp), stmtTyp.Underlying(), reflect.TypeOf(stmtTyp.Underlying())) + fmt.Fprintf(os.Stderr, "stmtType: %v %T | %v %T\n", stmtTyp, stmtTyp, stmtTyp.Underlying(), stmtTyp.Underlying()) } switch stmtTyp := stmtTyp.(type) { @@ -284,15 +370,15 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { stmtTypName) pass.Report(analysis.Diagnostic{ - Pos: result.Pos(), + Pos: result.Type.Pos(), Message: msg, SuggestedFixes: []analysis.SuggestedFix{ { Message: "Replace the interface return type with the concrete type", TextEdits: []analysis.TextEdit{ { - Pos: result.Pos(), - End: result.End(), + Pos: result.Type.Pos(), + End: result.Type.End(), NewText: []byte(stmtTypName), }, }, @@ -341,6 +427,10 @@ func fromSamePackage(pass *analysis.Pass, typ types.Type) bool { } func removePkgPrefix(typeStr string) string { + if len(typeStr) == 0 { + return typeStr + } + if typeStr[0] == '*' { return "*" + removePkgPrefix(typeStr[1:]) } @@ -351,3 +441,15 @@ func removePkgPrefix(typeStr string) string { return typeStr } + +func (r *runner) debugf(format string, a ...any) { + if r.debug { + fmt.Fprintf(os.Stderr, format, a...) + } +} + +func (r *runner) debugln(a ...any) { + if r.debug { + fmt.Fprintln(os.Stderr, a...) + } +} diff --git a/vendor/github.com/uudashr/iface/unexported/unexported.go b/vendor/github.com/uudashr/iface/unexported/unexported.go index 27a31ff1..c933d1b7 100644 --- a/vendor/github.com/uudashr/iface/unexported/unexported.go +++ b/vendor/github.com/uudashr/iface/unexported/unexported.go @@ -4,7 +4,8 @@ import ( "fmt" "go/ast" "go/types" - "reflect" + "os" + "strings" "github.com/uudashr/iface/internal/directive" "golang.org/x/tools/go/analysis" @@ -21,7 +22,7 @@ func newAnalyzer() *analysis.Analyzer { analyzer := &analysis.Analyzer{ Name: "unexported", Doc: "Detects interfaces which are not exported but are used as parameters or return values in exported functions or methods.", - URL: "https://pkg.go.dev/github.com/uudashr/iface/visibility", + URL: "https://pkg.go.dev/github.com/uudashr/iface/unexported", Requires: []*analysis.Analyzer{inspect.Analyzer}, Run: r.run, } @@ -47,11 +48,7 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { r.debugln("FuncDecl:", funcDecl.Name.Name) - dir := directive.ParseIgnore(funcDecl.Doc) - if dir != nil && dir.ShouldIgnore(pass.Analyzer.Name) { - // skip ignored function - r.debugln(" skip ignored") - + if directive.ShouldIgnore(funcDecl.Doc, pass.Analyzer.Name) { return } @@ -62,153 +59,199 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { if r.debug { infoType := pass.TypesInfo.TypeOf(recvType) - fmt.Println(" recvType:", recvType, "infoType:", infoType, "reflectType:", reflect.TypeOf(recvType)) + fmt.Fprintf(os.Stderr, " recvType: %v infoType: %v reflectType: %T\n", recvType, infoType, recvType) } - switch typ := recvType.(type) { - case *ast.Ident: - r.debugln(" recvIdent:", typ.Name) - - recvName = typ.Name - case *ast.StarExpr: - r.debugln(" recvStarExpr:", typ.X) - - if ident, ok := typ.X.(*ast.Ident); ok { - r.debugln(" recvIdent:", ident.Name) - recvName = ident.Name - } else { - r.debugln(" unhandled") - } - default: - r.debugln(" unhandled") + inner := recvType + if star, ok := inner.(*ast.StarExpr); ok { + inner = star.X } + + recvName = typeName(inner) } if !funcDecl.Name.IsExported() { // skip unexported functions - r.debugln(" skip non-exported") + r.debugln(" skip non-exported") return } - r.debugln(" params:") + r.debugln(" params:") - params := funcDecl.Type.Params + for _, param := range funcDecl.Type.Params.List { + r.checkType(pass, param.Type, funcDecl, recvName, "parameter") + } - for _, param := range params.List { - paramType := param.Type - infoType := pass.TypesInfo.TypeOf(paramType) + r.debugln(" results:") - r.debugln(" paramType:", paramType, "infoType:", infoType, "reflectType:", reflect.TypeOf(paramType)) + if funcDecl.Type.Results == nil { + r.debugln(" no results") - if !types.IsInterface(infoType) { - // skip non-interface - r.debugln(" skip non-interface") + return + } - continue - } + for _, result := range funcDecl.Type.Results.List { + r.checkType(pass, result.Type, funcDecl, recvName, "return value") + } + }) - if infoType.String() == "error" { - // skip error interface - r.debugln(" skip error interface") + return nil, nil +} - continue - } +// findIdent recursively unwraps StarExpr, Ellipsis, and ArrayType to locate the innermost +// *ast.Ident or *ast.SelectorExpr. Returns nil if not found. +func findIdent(expr ast.Expr) ast.Expr { + for { + switch e := expr.(type) { + case *ast.StarExpr: + expr = e.X + case *ast.Ellipsis: + expr = e.Elt + case *ast.ArrayType: + expr = e.Elt + case *ast.IndexExpr: + expr = e.X + case *ast.IndexListExpr: + expr = e.X + case *ast.ChanType: + expr = e.Value + case *ast.MapType: + expr = e.Value + case *ast.Ident, *ast.SelectorExpr: + return e + default: + return nil + } + } +} - if infoType.String() == "any" { - // skip any interface - r.debugln(" skip any interface") +// typeName reconstructs the type string as written in source from an AST expression. +func typeName(expr ast.Expr) string { + switch e := expr.(type) { + case *ast.ArrayType: + if e.Len == nil { + return "[]" + typeName(e.Elt) + } - continue + return "[" + typeName(e.Len) + "]" + typeName(e.Elt) + case *ast.BasicLit: + return e.Value + case *ast.Ident: + return e.Name + case *ast.StarExpr: + return "*" + typeName(e.X) + case *ast.Ellipsis: + return "..." + typeName(e.Elt) + case *ast.ChanType: + switch e.Dir { + case ast.SEND: + return "chan<- " + typeName(e.Value) + case ast.RECV: + return "<-chan " + typeName(e.Value) + default: + return "chan " + typeName(e.Value) + } + case *ast.MapType: + return "map[" + typeName(e.Key) + "]" + typeName(e.Value) + case *ast.SelectorExpr: + return typeName(e.X) + "." + e.Sel.Name + case *ast.IndexExpr: + return typeName(e.X) + "[" + typeName(e.Index) + "]" + case *ast.IndexListExpr: + var b strings.Builder + b.WriteString(typeName(e.X)) + b.WriteByte('[') + + for i, idx := range e.Indices { + if i > 0 { + b.WriteString(", ") } - switch typ := paramType.(type) { - case *ast.Ident: - if !typ.IsExported() { - r.debugln(" unexported") - - funcMethod := "function" - funcMethodName := funcDecl.Name.Name - - if recvName != "" { - funcMethod = "method" - funcMethodName = recvName + "." + funcDecl.Name.Name - } - - pass.Report(analysis.Diagnostic{ - Pos: typ.Pos(), - Message: fmt.Sprintf("unexported interface '%s' used as parameter in exported %s '%s'", typ.Name, funcMethod, funcMethodName), - }) - } - default: - r.debugln(" unhandled") - } + b.WriteString(typeName(idx)) } - r.debugln(" results:") + b.WriteByte(']') - results := funcDecl.Type.Results - if results == nil { - r.debugln(" no results") + return b.String() + default: + return fmt.Sprintf("%T", expr) + } +} + +func (r *runner) checkType(pass *analysis.Pass, expr ast.Expr, funcDecl *ast.FuncDecl, recvName, role string) { + infoType := pass.TypesInfo.TypeOf(expr) + + r.debugf(" %s: %v infoType: %v reflectType: %T\n", role, expr, infoType, expr) + + ident := findIdent(expr) + if ident == nil { + r.debugln(" skip non-interface") + + return + } + + switch typ := ident.(type) { + case *ast.SelectorExpr: + r.debugln(" external") + + return + case *ast.Ident: + if typ.IsExported() { + r.debugln(" skip exported") return } + } - for _, result := range results.List { - resultType := result.Type - infoType := pass.TypesInfo.TypeOf(resultType) + identType := pass.TypesInfo.TypeOf(ident) + if identType == nil { + r.debugln(" skip unknown type") - r.debugln(" resultType:", resultType, "infoType:", infoType, "reflectType:", reflect.TypeOf(resultType)) + return + } - if !types.IsInterface(infoType) { - r.debugln(" skip non-interface") + errorType := types.Universe.Lookup("error").Type() + anyType := types.Universe.Lookup("any").Type() - continue - } + if types.Identical(identType, errorType) || types.Identical(identType, anyType) { + r.debugln(" skip predeclared type") - if infoType.String() == "error" { - // skip error interface - r.debugln(" skip error interface") + return + } - continue - } + if !types.IsInterface(identType) { + r.debugln(" skip non-interface") - if infoType.String() == "any" { - // skip any interface - r.debugln(" skip any interface") + return + } - continue - } + typ := ident.(*ast.Ident) - switch typ := resultType.(type) { - case *ast.Ident: - if !typ.IsExported() { - r.debugln(" unexported") - - funcMethod := "function" - funcMethodName := funcDecl.Name.Name - - if recvName != "" { - funcMethod = "method" - funcMethodName = recvName + "." + funcDecl.Name.Name - } - - pass.Report(analysis.Diagnostic{ - Pos: typ.Pos(), - Message: fmt.Sprintf("unexported interface '%s' used as return value in exported %s '%s'", typ.Name, funcMethod, funcMethodName), - }) - } - default: - r.debugln(" unhandled") - } - } - }) + r.debugln(" unexported") - return nil, nil + funcMethod := "function" + funcMethodName := funcDecl.Name.Name + + if recvName != "" { + funcMethod = "method" + funcMethodName = recvName + "." + funcDecl.Name.Name + } + + pass.Report(analysis.Diagnostic{ + Pos: typ.Pos(), + Message: fmt.Sprintf("unexported interface '%s' used as %s in exported %s '%s'", typeName(expr), role, funcMethod, funcMethodName), + }) } func (r *runner) debugln(a ...any) { if r.debug { - fmt.Println(a...) + fmt.Fprintln(os.Stderr, a...) + } +} + +func (r *runner) debugf(format string, a ...any) { + if r.debug { + fmt.Fprintf(os.Stderr, format, a...) } } diff --git a/vendor/github.com/uudashr/iface/unused/unused.go b/vendor/github.com/uudashr/iface/unused/unused.go index 7783eeef..743e2942 100644 --- a/vendor/github.com/uudashr/iface/unused/unused.go +++ b/vendor/github.com/uudashr/iface/unused/unused.go @@ -4,7 +4,7 @@ import ( "fmt" "go/ast" "go/token" - "reflect" + "os" "slices" "strings" @@ -14,7 +14,7 @@ import ( "golang.org/x/tools/go/ast/inspector" ) -// Analyzer detects unused interfaces in the package. +// Analyzer detects interfaces which are not used anywhere in the same package where they are defined. var Analyzer = newAnalyzer() func newAnalyzer() *analysis.Analyzer { @@ -34,13 +34,27 @@ func newAnalyzer() *analysis.Analyzer { return analyzer } +type ifaceEntry struct { + ts *ast.TypeSpec + decl *ast.GenDecl +} + type runner struct { debug bool exclude string } func (r *runner) run(pass *analysis.Pass) (any, error) { - excludes := strings.Split(r.exclude, ",") + var excludes []string + + if r.exclude != "" { + for _, pkg := range strings.Split(r.exclude, ",") { + if p := strings.TrimSpace(pkg); p != "" { + excludes = append(excludes, p) + } + } + } + if slices.Contains(excludes, pass.Pkg.Path()) { return nil, nil } @@ -48,8 +62,7 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) // Collect all interface type declarations - ifaceDecls := make(map[string]*ast.TypeSpec) - genDecls := make(map[string]*ast.GenDecl) // ifaceName -> GenDecl + ifaces := make(map[string]ifaceEntry) nodeFilter := []ast.Node{ (*ast.GenDecl)(nil), @@ -62,17 +75,19 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { } if r.debug { - fmt.Printf("GenDecl: %v specs=%d\n", decl.Tok, len(decl.Specs)) + fmt.Fprintf(os.Stderr, "GenDecl: %v specs=%d\n", decl.Tok, len(decl.Specs)) } if decl.Tok != token.TYPE { return } + if directive.ShouldIgnore(decl.Doc, pass.Analyzer.Name) { + return + } + for i, spec := range decl.Specs { - if r.debug { - fmt.Printf(" spec[%d]: %v %v\n", i, spec, reflect.TypeOf(spec)) - } + r.debugf(" spec[%d]: %v %T\n", i, spec, spec) ts, ok := spec.(*ast.TypeSpec) if !ok { @@ -84,28 +99,23 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { continue } - if r.debug { - fmt.Println(" Interface type declaration:", ts.Name.Name, ts.Pos()) - } + r.debugln(" -> Interface type declaration:", ts.Name.Name, ts.Pos()) - dir := directive.ParseIgnore(decl.Doc) - if dir != nil && dir.ShouldIgnore(pass.Analyzer.Name) { - // skip due to ignore directive + if directive.ShouldIgnore(ts.Doc, pass.Analyzer.Name) { continue } - ifaceDecls[ts.Name.Name] = ts - genDecls[ts.Name.Name] = decl + ifaces[ts.Name.Name] = ifaceEntry{ts: ts, decl: decl} } }) if r.debug { var ifaceNames []string - for name := range ifaceDecls { + for name := range ifaces { ifaceNames = append(ifaceNames, name) } - fmt.Println("Declared interfaces:", ifaceNames) + fmt.Fprintln(os.Stderr, "Declared interfaces:", ifaceNames) } // Inspect whether the interface is used within the package @@ -119,32 +129,41 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { return } - ts, ok := ifaceDecls[ident.Name] + entry, ok := ifaces[ident.Name] if !ok { return } - if ts.Pos() == ident.Pos() { - // The identifier is the interface type declaration + if entry.ts.Pos() == ident.Pos() { return } - delete(ifaceDecls, ident.Name) - delete(genDecls, ident.Name) + delete(ifaces, ident.Name) }) if r.debug { - fmt.Printf("Package %s %s\n", pass.Pkg.Path(), pass.Pkg.Name()) + fmt.Fprintf(os.Stderr, "Package %s %s\n", pass.Pkg.Path(), pass.Pkg.Name()) } - for name, ts := range ifaceDecls { - decl := genDecls[name] + for name, entry := range ifaces { + ts := entry.ts + decl := entry.decl - var node ast.Node + var start, end token.Pos if len(decl.Specs) == 1 { - node = decl + start = decl.Pos() + if decl.Doc != nil { + start = decl.Doc.Pos() + } + + end = decl.End() } else { - node = ts + start = ts.Pos() + if ts.Doc != nil { + start = ts.Doc.Pos() + } + + end = ts.End() } msg := fmt.Sprintf("interface '%s' is declared but not used within the package", name) @@ -156,8 +175,8 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { Message: "Remove the unused interface declaration", TextEdits: []analysis.TextEdit{ { - Pos: node.Pos(), - End: node.End(), + Pos: start, + End: end, NewText: []byte{}, }, }, @@ -168,3 +187,15 @@ func (r *runner) run(pass *analysis.Pass) (any, error) { return nil, nil } + +func (r *runner) debugln(a ...any) { + if r.debug { + fmt.Fprintln(os.Stderr, a...) + } +} + +func (r *runner) debugf(format string, a ...any) { + if r.debug { + fmt.Fprintf(os.Stderr, format, a...) + } +} diff --git a/vendor/github.com/uudashr/iface/unusedmethod/doc.go b/vendor/github.com/uudashr/iface/unusedmethod/doc.go new file mode 100644 index 00000000..cef63a7f --- /dev/null +++ b/vendor/github.com/uudashr/iface/unusedmethod/doc.go @@ -0,0 +1,3 @@ +// Package unusedmethod defines an Analyzer that detects interface methods which +// are never used anywhere in the same package where they are defined. +package unusedmethod diff --git a/vendor/github.com/uudashr/iface/unusedmethod/unusedmethod.go b/vendor/github.com/uudashr/iface/unusedmethod/unusedmethod.go new file mode 100644 index 00000000..2f671465 --- /dev/null +++ b/vendor/github.com/uudashr/iface/unusedmethod/unusedmethod.go @@ -0,0 +1,226 @@ +package unusedmethod + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "os" + "slices" + "strings" + + "github.com/uudashr/iface/internal/directive" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +var Analyzer = newAnalyzer() + +func newAnalyzer() *analysis.Analyzer { + r := runner{} + + analyzer := &analysis.Analyzer{ + Name: "unusedmethod", + Doc: "Detects interface methods that are never used anywhere in the same package where they are defined.", + URL: "https://pkg.go.dev/github.com/uudashr/iface/unusedmethod", + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: r.run, + } + + analyzer.Flags.BoolVar(&r.debug, "nerd", false, "enable nerd mode") + analyzer.Flags.StringVar(&r.exclude, "exclude", "", "comma-separated list of packages to exclude from the check") + + return analyzer +} + +type methodEntry struct { + ifaceName string + field *ast.Field +} + +type runner struct { + debug bool + exclude string +} + +func (r *runner) run(pass *analysis.Pass) (any, error) { + var excludes []string + + if r.exclude != "" { + for _, pkg := range strings.Split(r.exclude, ",") { + if p := strings.TrimSpace(pkg); p != "" { + excludes = append(excludes, p) + } + } + } + + if slices.Contains(excludes, pass.Pkg.Path()) { + return nil, nil + } + + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + unusedMethods := make(map[*types.Func]methodEntry) + + nodeFilter := []ast.Node{ + (*ast.GenDecl)(nil), + } + + inspect.Preorder(nodeFilter, func(n ast.Node) { + decl, ok := n.(*ast.GenDecl) + if !ok { + return + } + + if r.debug { + fmt.Fprintf(os.Stderr, "GenDecl: %v specs=%d\n", decl.Tok, len(decl.Specs)) + } + + if decl.Tok != token.TYPE { + return + } + + if directive.ShouldIgnore(decl.Doc, pass.Analyzer.Name) { + return + } + + for i, spec := range decl.Specs { + r.debugf(" spec[%d]: %v %T\n", i, spec, spec) + + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + + ifaceType, ok := ts.Type.(*ast.InterfaceType) + if !ok { + continue + } + + r.debugln(" -> Interface type declaration:", ts.Name.Name) + + if directive.ShouldIgnore(ts.Doc, pass.Analyzer.Name) { + continue + } + + for j, field := range ifaceType.Methods.List { + switch ft := field.Type.(type) { + case *ast.FuncType: + name := field.Names[0] + obj := pass.TypesInfo.Defs[name] + + if r.debug { + fmt.Fprintf(os.Stderr, " [%d] Field: func %s %T\n", j, name.Name, ft) + fmt.Fprintf(os.Stderr, " obj: %v %T %p\n", obj, obj, obj) + } + + if directive.ShouldIgnore(field.Doc, pass.Analyzer.Name) { + continue + } + + if directive.ShouldIgnore(field.Comment, pass.Analyzer.Name) { + continue + } + + if fn, ok := obj.(*types.Func); ok { + unusedMethods[fn] = methodEntry{ + ifaceName: ts.Name.Name, + field: field, + } + } + default: + r.debugf(" [%d] Field: unknown %v %T\n", j, ft, ft) + } + } + } + }) + + // Method is always SelectorExpr + nodeFilter = []ast.Node{ + (*ast.SelectorExpr)(nil), + } + + r.debugln("Method usage") + inspect.Preorder(nodeFilter, func(n ast.Node) { + r.debugf(" n %v %T\n", n, n) + + selExp, ok := n.(*ast.SelectorExpr) + if !ok { + return + } + + r.debugf(" selExp sel: %v %T, x: %v %T\n", selExp.Sel, selExp.Sel, selExp.X, selExp.X) + + sel, ok := pass.TypesInfo.Selections[selExp] + if !ok { + return + } + + if r.debug { + fmt.Fprintf(os.Stderr, " sel -> %v %T, kind: %v %T, obj: %v %T\n", sel, sel, sel.Kind(), sel.Kind(), sel.Obj(), sel.Obj()) + } + + fn, ok := sel.Obj().(*types.Func) + if !ok { + return + } + + delete(unusedMethods, fn) + }) + + if r.debug { + fmt.Fprintf(os.Stderr, "Unused methods %d\n", len(unusedMethods)) + } + + for fn, entry := range unusedMethods { + if r.debug { + fmt.Fprintf(os.Stderr, " %v %T %s\n", fn, fn, fn.Name()) + } + + msg := fmt.Sprintf("method '%s()' is declared on interface '%s' but not used within the package", fn.Name(), entry.ifaceName) + + field := entry.field + + pos := field.Pos() + if doc := field.Doc; doc != nil { + pos = doc.Pos() + } + + end := field.End() + if comment := field.Comment; comment != nil { + end = comment.End() + } + + pass.Report(analysis.Diagnostic{ + Pos: field.Pos(), + Message: msg, + SuggestedFixes: []analysis.SuggestedFix{ + { + Message: "Remove the unused method", + TextEdits: []analysis.TextEdit{ + { + Pos: pos, + End: end, + NewText: []byte{}, + }, + }, + }, + }, + }) + } + + return nil, nil +} + +func (r *runner) debugln(a ...any) { + if r.debug { + fmt.Fprintln(os.Stderr, a...) + } +} + +func (r *runner) debugf(format string, a ...any) { + if r.debug { + fmt.Fprintf(os.Stderr, format, a...) + } +} diff --git a/vendor/go-simpler.org/sloglint/.golangci.yaml b/vendor/go-simpler.org/sloglint/.golangci.yaml index cbac9150..526e368d 100644 --- a/vendor/go-simpler.org/sloglint/.golangci.yaml +++ b/vendor/go-simpler.org/sloglint/.golangci.yaml @@ -1,10 +1,10 @@ -# https://golangci-lint.run/usage/configuration +# https://golangci-lint.run/docs/configuration/file version: "2" linters: - default: standard enable: - gocritic + - modernize settings: gocritic: enable-all: true @@ -16,3 +16,6 @@ formatters: enable: - gofumpt - goimports + settings: + gofumpt: + extra-rules: true diff --git a/vendor/go-simpler.org/sloglint/.goreleaser.yaml b/vendor/go-simpler.org/sloglint/.goreleaser.yaml new file mode 100644 index 00000000..a8fa6bbf --- /dev/null +++ b/vendor/go-simpler.org/sloglint/.goreleaser.yaml @@ -0,0 +1,12 @@ +# https://goreleaser.com/customization/builds/go + +builds: + - main: ./cmd/{{.ProjectName}} + flags: + - -trimpath + ldflags: + - -s -w -X main.version={{.Version}} + env: + - CGO_ENABLED=0 + targets: + - go_first_class # https://go.dev/wiki/PortingPolicy#first-class-ports diff --git a/vendor/go-simpler.org/sloglint/.goreleaser.yml b/vendor/go-simpler.org/sloglint/.goreleaser.yml deleted file mode 100644 index d31ea11d..00000000 --- a/vendor/go-simpler.org/sloglint/.goreleaser.yml +++ /dev/null @@ -1,18 +0,0 @@ -builds: - - main: ./cmd/sloglint - 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 diff --git a/vendor/go-simpler.org/sloglint/Makefile b/vendor/go-simpler.org/sloglint/Makefile index 6165b16f..cf5eb9d1 100644 --- a/vendor/go-simpler.org/sloglint/Makefile +++ b/vendor/go-simpler.org/sloglint/Makefile @@ -1,28 +1,25 @@ .POSIX: .SUFFIXES: -all: test lint +help: + @echo 'Available commands:' + @echo ' build Build the project' + @echo ' fmt Run formatters' + @echo ' lint Run linters' + @echo ' test Run tests' + @echo ' test/cover Run tests and open coverage report' -test: - go test -race -shuffle=on -cover ./... +build: + @go build -o /dev/null ./... -test/cover: - go test -race -shuffle=on -coverprofile=coverage.out ./... - go tool cover -html=coverage.out +fmt: + @golangci-lint fmt lint: - golangci-lint run - -tidy: - go mod tidy + @golangci-lint run --fix -generate: - go generate ./... - -# run `make pre-commit` once to install the hook. -pre-commit: .git/hooks/pre-commit test lint tidy generate - git diff --exit-code +test: + @go test -race -shuffle=on -coverprofile=coverage.out ./... -.git/hooks/pre-commit: - echo "make pre-commit" > .git/hooks/pre-commit - chmod +x .git/hooks/pre-commit +test/cover: test + @go tool cover -html=coverage.out diff --git a/vendor/go-simpler.org/sloglint/README.md b/vendor/go-simpler.org/sloglint/README.md index e75fd021..9653bd15 100644 --- a/vendor/go-simpler.org/sloglint/README.md +++ b/vendor/go-simpler.org/sloglint/README.md @@ -1,205 +1,296 @@ # sloglint -[![checks](https://github.com/go-simpler/sloglint/actions/workflows/checks.yml/badge.svg)](https://github.com/go-simpler/sloglint/actions/workflows/checks.yml) -[![pkg.go.dev](https://pkg.go.dev/badge/go-simpler.org/sloglint.svg)](https://pkg.go.dev/go-simpler.org/sloglint) -[![goreportcard](https://goreportcard.com/badge/go-simpler.org/sloglint)](https://goreportcard.com/report/go-simpler.org/sloglint) +[![checks](https://github.com/go-simpler/sloglint/actions/workflows/checks.yaml/badge.svg)](https://github.com/go-simpler/sloglint/actions/workflows/checks.yaml) +[![docs](https://pkg.go.dev/badge/go-simpler.org/sloglint.svg)](https://pkg.go.dev/go-simpler.org/sloglint) [![codecov](https://codecov.io/gh/go-simpler/sloglint/branch/main/graph/badge.svg)](https://codecov.io/gh/go-simpler/sloglint) A Go linter that ensures consistent code style when using `log/slog`. -## 📌 About +## Install -The `log/slog` API allows two different types of arguments: key-value pairs and attributes. -While people may have different opinions about which one is better, most seem to agree on one thing: it should be consistent. -With `sloglint` you can enforce various rules for `log/slog` based on your preferred code style. - -## 🚀 Features - -* Enforce not mixing key-value pairs and attributes (default) -* Enforce using either key-value pairs only or attributes only (optional) -* Enforce not using global loggers (optional) -* Enforce using methods that accept a context (optional) -* Enforce using static messages (optional) -* Enforce message style (optional) -* Enforce using constants instead of raw keys (optional) -* Enforce key naming convention (optional) -* Enforce not using specific keys (optional) -* Enforce putting arguments on separate lines (optional) - -## 📦 Install - -`sloglint` is integrated into [`golangci-lint`][1], and this is the recommended way to use it. - -To enable the linter, add the following lines to `.golangci.yml`: +`sloglint` is part of [golangci-lint](https://golangci-lint.run), and this is the recommended way to use it. ```yaml +# .golangci.yaml linters: enable: - sloglint ``` -Alternatively, you can download a prebuilt binary from the [Releases][2] page to use `sloglint` standalone. +Alternatively, you can download a prebuilt binary from the [Releases](https://github.com/go-simpler/sloglint/releases) page to use `sloglint` standalone. -## 📋 Usage +## Supported checks -Run `golangci-lint` with `sloglint` enabled. -See the list of [available options][3] to configure the linter. +For `log/slog` functions: +- [No global logger](#no-global-logger) +- [Context only](#context-only) +- [Discard handler](#discard-handler) -When using `sloglint` standalone, pass the options as flags of the same name. +For log messages: +- [Static message](#static-message) +- [Message style](#message-style) -### No mixed arguments +For log arguments: +- [No mixed arguments](#no-mixed-arguments) +- [Key-value pairs only](#key-value-pairs-only) +- [Attributes only](#attributes-only) +- [Arguments on separate lines](#arguments-on-separate-lines) -The `no-mixed-args` option causes `sloglint` to report mixing key-values pairs and attributes within a single function call: +For log keys: +- [Constant keys](#constant-keys) +- [Allowed keys](#allowed-keys) +- [Forbidden keys](#forbidden-keys) +- [Key naming case](#key-naming-case) -```go -slog.Info("a user has logged in", "user_id", 42, slog.String("ip_address", "192.0.2.0")) // sloglint: key-value pairs and attributes should not be mixed -``` +The checks for log messages, arguments, and keys can also be used to analyze [custom functions](#custom-function-analysis). -It is enabled by default. - -### Key-value pairs only +### No global logger -The `kv-only` option causes `sloglint` to report any use of attributes: +Report the use of global loggers. +Alternatively, only report the use of the `slog.Default()` logger. ```go -slog.Info("a user has logged in", slog.Int("user_id", 42)) // sloglint: attributes should not be used +slog.Info("a user has logged in") +// sloglint: global logger should not be used ``` -### Attributes only - -In contrast, the `attr-only` option causes `sloglint` to report any use of key-value pairs: - -```go -slog.Info("a user has logged in", "user_id", 42) // sloglint: key-value pairs should not be used +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + no-global: "all" # Or "default". ``` -### No global +### Context only -Some projects prefer to pass loggers as explicit dependencies. -The `no-global` option causes `sloglint` to report the use of global loggers. +Report the use of functions without a `context.Context`. +Alternatively, only report their use if a context exists within the scope of the outermost function. ```go -slog.Info("a user has logged in", "user_id", 42) // sloglint: global logger should not be used +slog.Info("a user has logged in") +// sloglint: InfoContext should be used instead +``` + +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + context: "all" # Or "scope". ``` -Possible values are `all` (report all global loggers) and `default` (report only the default `slog` logger). +This check partially supports autofix. -### Context only +### Discard handler -Some `slog.Handler` implementations make use of the given `context.Context` (e.g. to access context values). -For them to work properly, you need to pass a context to all logger calls. -The `context-only` option causes `sloglint` to report the use of methods without a context: +Suggest using `slog.DiscardHandler` when possible. ```go -slog.Info("a user has logged in") // sloglint: InfoContext should be used instead +slog.NewJSONHandler(io.Discard, nil) +// sloglint: use slog.DiscardHandler instead ``` -Possible values are `all` (report all contextless calls) and `scope` (report only if a context exists in the scope of the outermost function). +This check is enabled by default and supports autofix. -### Static messages +### Static message -To get the most out of structured logging, you may want to require log messages to be static. -The `static-msg` option causes `sloglint` to report non-static messages: +Report dynamic log messages, such as those that are built with `fmt.Sprintf`. ```go -slog.Info(fmt.Sprintf("a user with id %d has logged in", 42)) // sloglint: message should be a string literal or a constant +slog.Info(fmt.Sprintf("a user with id %d has logged in", 42)) +// sloglint: message should be a string literal or a constant +``` + +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + static-msg: true ``` -The report can be fixed by moving dynamic values to arguments: +### Message style + +Report log messages that do not match a particular style. +The supported styles are `lowercased` (the first letter is lowercase) and `capitalized` (the first letter is uppercase). ```go -slog.Info("a user has logged in", "user_id", 42) +slog.Info("A user has logged in") +// sloglint: message should be lowercased ``` -### Message style +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + msg-style: "lowercased" # Or "capitalized". +``` -The `msg-style` option causes `sloglint` to check log messages for a particular style. +### No mixed arguments -Possible values are `lowercased` (report messages that begin with an uppercase letter)... +Report the use of both key-value pairs and attributes within a single function call. ```go -slog.Info("Msg") // sloglint: message should be lowercased +slog.Info("a user has logged in", "user_id", 42, slog.String("ip_address", "192.0.2.0")) +// sloglint: key-value pairs and attributes should not be mixed ``` -...and `capitalized` (report messages that begin with a lowercase letter): +This check is enabled by default. + +### Key-value pairs only + +Report any use of attributes as function call arguments. ```go -slog.Info("msg") // sloglint: message should be capitalized +slog.Info("a user has logged in", slog.Int("user_id", 42)) +// sloglint: attributes should not be used ``` -Special cases such as acronyms (e.g. `HTTP`, `U.S.`) are ignored. +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + kv-only: true +``` -### No raw keys +### Attributes only -To prevent typos, you may want to forbid the use of raw keys altogether. -The `no-raw-keys` option causes `sloglint` to report the use of strings as keys -(including `slog.Attr` calls, e.g. `slog.Int("user_id", 42)`): +Report any use of key-value pairs as function call arguments. ```go -slog.Info("a user has logged in", "user_id", 42) // sloglint: raw keys should not be used +slog.Info("a user has logged in", "user_id", 42) +// sloglint: key-value pairs should not be used +``` + +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + attr-only: true ``` -This report can be fixed by using either constants... +### Arguments on separate lines + +Report two or more arguments on the same line. +A key-value pair is considered a single argument. ```go -const UserId = "user_id" +slog.Info("a user has logged in", "user_id", 42, "ip_address", "192.0.2.0") +// sloglint: arguments should be put on separate lines +``` -slog.Info("a user has logged in", UserId, 42) +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + args-on-sep-lines: true ``` -...or custom `slog.Attr` constructors: +### Constant keys -```go -func UserId(value int) slog.Attr { return slog.Int("user_id", value) } +Report the use of string literals as log keys. -slog.Info("a user has logged in", UserId(42)) +```go +slog.Info("a user has logged in", "user_id", 42) +// sloglint: the "user_id" key should be a constant ``` -> [!TIP] -> Such helpers can be automatically generated for you by the [`sloggen`][4] tool. Give it a try too! +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + no-raw-keys: true +``` -### Key naming convention +### Allowed keys -To ensure consistency in logs, you may want to enforce a single key naming convention. -The `key-naming-case` option causes `sloglint` to report keys written in a case other than the given one: +Report the use of log keys that are not explicitly allowed. ```go -slog.Info("a user has logged in", "user-id", 42) // sloglint: keys should be written in snake_case +slog.Info("a user has logged in", "id", 42) +// sloglint: the "id" key is not allowed and should not be used ``` -Possible values are `snake`, `kebab`, `camel`, or `pascal`. +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + allowed-keys: + - user_id +``` ### Forbidden keys -To prevent accidental use of reserved log keys, you may want to forbid specific keys altogether. -The `forbidden-keys` option causes `sloglint` to report the use of forbidden keys: +Report the use of forbidden log keys. +When using the standard `slog.JSONHandler` or `slog.TextHandler`, +you may want to forbid the `time`, `level`, `msg`, and `source` keys, +as these will be written by the handler. ```go -slog.Info("a user has logged in", "reserved", 42) // sloglint: "reserved" key is forbidden and should not be used +slog.Info("a user has logged in", "time", time.Now()) +// sloglint: the "time" key is forbidden and should not be used ``` -For example, when using the standard `slog.JSONHandler` and `slog.TextHandler`, -you may want to forbid the `time`, `level`, `msg`, and `source` keys, as these are used by the handlers. +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + forbidden-keys: + - time + - level + - msg + - source +``` -### Arguments on separate lines +### Key naming case -To improve code readability, you may want to put arguments on separate lines, especially when using key-value pairs. -The `args-on-sep-lines` option causes `sloglint` to report 2+ arguments on the same line: +Report log keys that do not match a particular naming case. +The supported cases are `snake_case`, `kebab-case`, `camelCase`, and `PascalCase`. ```go -slog.Info("a user has logged in", "user_id", 42, "ip_address", "192.0.2.0") // sloglint: arguments should be put on separate lines +slog.Info("a user has logged in", "user-id", 42) +// sloglint: keys should be written in snake_case ``` -This report can be fixed by reformatting the code: - -```go -slog.Info("a user has logged in", - "user_id", 42, - "ip_address", "192.0.2.0", -) +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + key-naming-case: "snake" # Or "kebab", "camel", "pascal". ``` -[1]: https://golangci-lint.run -[2]: https://github.com/go-simpler/sloglint/releases -[3]: https://golangci-lint.run/usage/linters/#sloglint -[4]: https://github.com/go-simpler/sloggen +This check supports autofix. + +## Custom function analysis + +Analyze custom functions in addition to the standard `log/slog` functions. + +The following function properties must be specified: +1. The full name of the function, including the package, e.g. `log/slog.Info`. +If the function is a method, the receiver type must be wrapped in parentheses, e.g. `(*log/slog.Logger).Info`. +2. The position of the `msg string` argument in the function signature, starting from 0. +If there is no message in the function, a negative value must be passed. +3. The position of the `args ...any` argument in the function signature, starting from 0. +If there are no arguments in the function, a negative value must be passed. + +Here's an example for the [exp/slog](https://pkg.go.dev/golang.org/x/exp/slog) package, the predecessor of `log/slog`. + +```yaml +# .golangci.yaml +linters: + settings: + sloglint: + custom-funcs: + - name: "(*golang.org/x/exp/slog.Logger).InfoContext" + msg-pos: 1 + args-pos: 2 +``` diff --git a/vendor/go-simpler.org/sloglint/analyzer.go b/vendor/go-simpler.org/sloglint/analyzer.go new file mode 100644 index 00000000..03e43e02 --- /dev/null +++ b/vendor/go-simpler.org/sloglint/analyzer.go @@ -0,0 +1,209 @@ +// Package sloglint implements the sloglint analyzer. +package sloglint + +import ( + "go/ast" + "go/version" + "slices" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/go/types/typeutil" +) + +// New creates a new sloglint analyzer. +func New(opts *Options) *analysis.Analyzer { + if opts == nil { + opts = &Options{NoMixedArguments: true} + } + + return &analysis.Analyzer{ + Name: "sloglint", + Doc: "Ensures consistent code style when using log/slog.", + URL: "https://go-simpler.org/sloglint", + Flags: flags(opts), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: func(pass *analysis.Pass) (any, error) { + if err := opts.validate(); err != nil { + return nil, err + } + + root := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Root() + for cursor := range root.Preorder(new(ast.CallExpr), new(ast.CompositeLit)) { + analyzeNode(pass, opts, cursor) + } + + return nil, nil + }, + } +} + +var slogFuncs = []Func{ + {"log/slog.Log", 2, 3}, + {"log/slog.LogAttrs", 2, 3}, + {"log/slog.Debug", 0, 1}, + {"log/slog.Info", 0, 1}, + {"log/slog.Warn", 0, 1}, + {"log/slog.Error", 0, 1}, + {"log/slog.DebugContext", 1, 2}, + {"log/slog.InfoContext", 1, 2}, + {"log/slog.WarnContext", 1, 2}, + {"log/slog.ErrorContext", 1, 2}, + {"log/slog.With", -1, 0}, + {"log/slog.Group", -1, 1}, + {"log/slog.NewTextHandler", -1, -1}, + {"log/slog.NewJSONHandler", -1, -1}, + {"(*log/slog.Logger).Log", 2, 3}, + {"(*log/slog.Logger).LogAttrs", 2, 3}, + {"(*log/slog.Logger).Debug", 0, 1}, + {"(*log/slog.Logger).Info", 0, 1}, + {"(*log/slog.Logger).Warn", 0, 1}, + {"(*log/slog.Logger).Error", 0, 1}, + {"(*log/slog.Logger).DebugContext", 1, 2}, + {"(*log/slog.Logger).InfoContext", 1, 2}, + {"(*log/slog.Logger).WarnContext", 1, 2}, + {"(*log/slog.Logger).ErrorContext", 1, 2}, + {"(*log/slog.Logger).With", -1, 0}, +} + +func analyzeNode(pass *analysis.Pass, opts *Options, cursor inspector.Cursor) { + node := cursor.Node() + if cl, ok := node.(*ast.CompositeLit); ok && typeName(pass.TypesInfo, cl) == "log/slog.Attr" { + analyzeAttrKey(pass, opts, cl) + return + } + + call, ok := node.(*ast.CallExpr) + if !ok { + return + } + + fn := typeutil.StaticCallee(pass.TypesInfo, call) + if fn == nil { + return + } + + switch fn.FullName() { + case "log/slog.Int", + "log/slog.Int64", + "log/slog.Uint64", + "log/slog.Float64", + "log/slog.String", + "log/slog.Bool", + "log/slog.Time", + "log/slog.Duration", + "log/slog.Any": + analyzeKey(pass, opts, call.Args[0]) + return + case "log/slog.Group": + analyzeKey(pass, opts, call.Args[0]) + // Special case: don't return here, we also need to analyze the group's arguments. + } + + funcs := slices.Concat(slogFuncs, opts.CustomFuncs) + idx := slices.IndexFunc(funcs, func(f Func) bool { + return f.FullName == fn.FullName() + }) + if idx == -1 { + return + } + + if idx < len(slogFuncs) { + analyzeFunction(pass, opts, call, cursor) + } + if pos := funcs[idx].MessagePos; pos >= 0 && len(call.Args) > pos { + analyzeMessage(pass, opts, call.Args[pos]) + } + if pos := funcs[idx].ArgumentsPos; pos >= 0 && len(call.Args) > pos { + analyzeArguments(pass, opts, call.Args[pos:]) + } +} + +func analyzeFunction(pass *analysis.Pass, opts *Options, call *ast.CallExpr, cursor inspector.Cursor) { + if opts.NoGlobalLogger != "" { + noGlobalLogger(pass, call, opts.NoGlobalLogger == noGlobalLoggerDefault) + } + if opts.ContextOnly != "" { + contextOnly(pass, call, cursor, opts.ContextOnly == contextOnlyScope) + } + v := pass.Module.GoVersion // Empty in test runs. + if v == "" || version.Compare("go"+v, "go1.24") >= 0 { + discardHandler(pass, call) + } +} + +func analyzeMessage(pass *analysis.Pass, opts *Options, msg ast.Expr) { + if opts.StaticMessage { + staticMessage(pass, msg) + } + if opts.MessageStyle != "" { + messageStyle(pass, msg, opts.MessageStyle) + } +} + +func analyzeArguments(pass *analysis.Pass, opts *Options, args []ast.Expr) { + var keys, attrs []ast.Expr + + for i := 0; i < len(args); i++ { + typ := pass.TypesInfo.TypeOf(args[i]) + if typ == nil { + continue + } + switch typ.String() { + case "string": + keys = append(keys, args[i]) + analyzeKey(pass, opts, args[i]) + i++ // Skip the value. + case "log/slog.Attr": + attrs = append(attrs, args[i]) + case "[]any", "[]log/slog.Attr": + continue // The last argument may be an unpacked slice, skip it. + } + } + + if opts.NoMixedArguments { + noMixedArguments(pass, keys, attrs) + } + if opts.KeyValuePairsOnly { + keyValuePairsOnly(pass, attrs) + } + if opts.AttributesOnly { + attributesOnly(pass, keys) + } + if opts.ArgumentsOnSeparateLines { + argumentsOnSeparateLines(pass, keys, attrs) + } +} + +func analyzeKey(pass *analysis.Pass, opts *Options, key ast.Expr) { + if opts.ConstantKeys { + constantKeys(pass, key) + } + if opts.KeyNamingCase != "" { + keyNamingCase(pass, key, opts.KeyNamingCase) + } + if len(opts.AllowedKeys) > 0 { + allowedKeys(pass, key, opts.AllowedKeys) + } + if len(opts.ForbiddenKeys) > 0 { + forbiddenKeys(pass, key, opts.ForbiddenKeys) + } +} + +func analyzeAttrKey(pass *analysis.Pass, opts *Options, attr *ast.CompositeLit) { + switch len(attr.Elts) { + case 1: + if kv := attr.Elts[0].(*ast.KeyValueExpr); kv.Key.(*ast.Ident).Name == "Key" { + analyzeKey(pass, opts, kv.Value) // slog.Attr{Key: ...} + } + case 2: + if kv, ok := attr.Elts[0].(*ast.KeyValueExpr); ok && kv.Key.(*ast.Ident).Name == "Key" { + analyzeKey(pass, opts, kv.Value) // slog.Attr{Key: ..., Value: ...} + } else if kv, ok := attr.Elts[1].(*ast.KeyValueExpr); ok && kv.Key.(*ast.Ident).Name == "Key" { + analyzeKey(pass, opts, kv.Value) // slog.Attr{Value: ..., Key: ...} + } else { + analyzeKey(pass, opts, attr.Elts[0]) // slog.Attr{..., ...} + } + } +} diff --git a/vendor/go-simpler.org/sloglint/argument_checks.go b/vendor/go-simpler.org/sloglint/argument_checks.go new file mode 100644 index 00000000..17590d7e --- /dev/null +++ b/vendor/go-simpler.org/sloglint/argument_checks.go @@ -0,0 +1,55 @@ +package sloglint + +import ( + "go/ast" + "slices" + + "golang.org/x/tools/go/analysis" +) + +func noMixedArguments(pass *analysis.Pass, keys, attrs []ast.Expr) { + if len(keys) == 0 { + return + } + for _, attr := range attrs { + if call, ok := attr.(*ast.CallExpr); ok && funcName(pass.TypesInfo, call) == "log/slog.Group" { + continue // Special case: slog.Group() should always be allowed. + } + pass.ReportRangef(attr, "key-value pairs and attributes should not be mixed") + return + } +} + +func keyValuePairsOnly(pass *analysis.Pass, attrs []ast.Expr) { + for _, attr := range attrs { + if call, ok := attr.(*ast.CallExpr); ok && funcName(pass.TypesInfo, call) == "log/slog.Group" { + continue // Special case: slog.Group() should always be allowed. + } + pass.ReportRangef(attr, "attributes should not be used") + return + } +} + +func attributesOnly(pass *analysis.Pass, keys []ast.Expr) { + for _, key := range keys { + pass.ReportRangef(key, "key-value pairs should not be used") + return + } +} + +func argumentsOnSeparateLines(pass *analysis.Pass, keys, attrs []ast.Expr) { + args := slices.Concat(keys, attrs) + if len(args) <= 1 { + return // Special case: slog.Info("msg", "key", "value") is fine. + } + + prevLine := pass.Fset.Position(args[0].Pos()).Line + for _, arg := range args[1:] { + currLine := pass.Fset.Position(arg.Pos()).Line + if currLine == prevLine { + pass.Reportf(arg.Pos(), "arguments should be put on separate lines") + return + } + prevLine = currLine + } +} diff --git a/vendor/go-simpler.org/sloglint/function_checks.go b/vendor/go-simpler.org/sloglint/function_checks.go new file mode 100644 index 00000000..7781aba6 --- /dev/null +++ b/vendor/go-simpler.org/sloglint/function_checks.go @@ -0,0 +1,144 @@ +package sloglint + +import ( + "fmt" + "go/ast" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/go/types/typeutil" +) + +func noGlobalLogger(pass *analysis.Pass, call *ast.CallExpr, defaultOnly bool) { + fn := typeutil.StaticCallee(pass.TypesInfo, call) + + switch fn.Name() { + case "Log", "LogAttrs", + "Debug", "Info", "Warn", "Error", + "DebugContext", "InfoContext", "WarnContext", "ErrorContext", + "With": + default: + return + } + + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return + } + + ident, ok := sel.X.(*ast.Ident) + if !ok { + return + } + + if ident.Name == "slog" { + pass.ReportRangef(sel.X, "default logger should not be used") + return + } + + if defaultOnly { + return + } + + if obj := pass.TypesInfo.ObjectOf(ident); obj != nil && obj.Parent() == obj.Pkg().Scope() { + pass.ReportRangef(sel.X, "global logger should not be used") + } +} + +func contextOnly(pass *analysis.Pass, call *ast.CallExpr, cursor inspector.Cursor, scopeOnly bool) { + fn := typeutil.StaticCallee(pass.TypesInfo, call) + + switch fn.Name() { + case "Debug", "Info", "Warn", "Error": + default: + return + } + + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return + } + + if !scopeOnly { + // Don't suggest fixes here, we don't know whether there is a context in the scope. + pass.ReportRangef(sel.Sel, "%sContext should be used instead", fn.Name()) + return + } + + for cursor := range cursor.Enclosing(new(ast.FuncDecl), new(ast.FuncLit)) { + var params []*ast.Field + switch fn := cursor.Node().(type) { + case *ast.FuncDecl: + params = fn.Type.Params.List + case *ast.FuncLit: + params = fn.Type.Params.List + } + + if len(params) == 0 { + continue + } + + for _, param := range params { + if len(param.Names) == 0 { + continue + } + + var ctxArg string + switch name := param.Names[0]; typeName(pass.TypesInfo, name) { + case "context.Context": + ctxArg = name.Name + case "*net/http.Request": + ctxArg = name.Name + ".Context()" + default: + continue + } + + pass.Report(analysis.Diagnostic{ + Pos: sel.Sel.Pos(), + End: sel.Sel.End(), + Message: fmt.Sprintf("%sContext should be used instead", fn.Name()), + SuggestedFixes: []analysis.SuggestedFix{{ + TextEdits: []analysis.TextEdit{{ + Pos: sel.Sel.Pos(), + End: call.Lparen + 1, + NewText: fmt.Appendf(nil, "%sContext(%s, ", fn.Name(), ctxArg), + }}, + }}, + }) + return + } + } +} + +func discardHandler(pass *analysis.Pass, call *ast.CallExpr) { + if len(call.Args) == 0 { + return + } + + sel, ok := call.Args[0].(*ast.SelectorExpr) + if !ok { + return + } + + obj := pass.TypesInfo.ObjectOf(sel.Sel) + if obj == nil { + return + } + + if obj.Pkg().Name() != "io" || obj.Name() != "Discard" { + return + } + + pass.Report(analysis.Diagnostic{ + Pos: call.Pos(), + End: call.Pos(), + Message: "use slog.DiscardHandler instead", + SuggestedFixes: []analysis.SuggestedFix{{ + TextEdits: []analysis.TextEdit{{ + Pos: call.Pos(), + End: call.End(), + NewText: []byte("slog.DiscardHandler"), + }}, + }}, + }) +} diff --git a/vendor/go-simpler.org/sloglint/key_checks.go b/vendor/go-simpler.org/sloglint/key_checks.go new file mode 100644 index 00000000..c664a842 --- /dev/null +++ b/vendor/go-simpler.org/sloglint/key_checks.go @@ -0,0 +1,73 @@ +package sloglint + +import ( + "fmt" + "go/ast" + "go/types" + "slices" + "strconv" + + "github.com/ettle/strcase" + "golang.org/x/tools/go/analysis" +) + +func constantKeys(pass *analysis.Pass, key ast.Expr) { + if sel, ok := key.(*ast.SelectorExpr); ok { + key = sel.Sel // The key is defined in another package, e.g. pkg.ConstKey. + } + if ident, ok := key.(*ast.Ident); ok { + if _, ok := pass.TypesInfo.ObjectOf(ident).(*types.Const); ok { + return + } + } + name, _ := keyName(key) + pass.ReportRangef(key, "the %q key should be a constant", name) +} + +func allowedKeys(pass *analysis.Pass, key ast.Expr, allowed []string) { + if name, ok := keyName(key); ok && !slices.Contains(allowed, name) { + pass.ReportRangef(key, "the %q key is not allowed and should not be used", name) + } +} + +func forbiddenKeys(pass *analysis.Pass, key ast.Expr, forbidden []string) { + if name, ok := keyName(key); ok && slices.Contains(forbidden, name) { + pass.ReportRangef(key, "the %q key is forbidden and should not be used", name) + } +} + +func keyNamingCase(pass *analysis.Pass, key ast.Expr, caseName string) { + name, ok := keyName(key) + if !ok { + return + } + + var caseFn func(string) string + switch caseName { + case keyNamingCaseSnake: + caseFn = strcase.ToSnake + case keyNamingCaseKebab: + caseFn = strcase.ToKebab + case keyNamingCaseCamel: + caseFn = strcase.ToCamel + case keyNamingCasePascal: + caseFn = strcase.ToPascal + } + + if name == caseFn(name) { + return + } + + pass.Report(analysis.Diagnostic{ + Pos: key.Pos(), + End: key.End(), + Message: fmt.Sprintf("keys should be written in %s", caseFn(caseName+" case")), + SuggestedFixes: []analysis.SuggestedFix{{ + TextEdits: []analysis.TextEdit{{ + Pos: key.Pos(), + End: key.End(), + NewText: strconv.AppendQuote(nil, caseFn(name)), + }}, + }}, + }) +} diff --git a/vendor/go-simpler.org/sloglint/message_checks.go b/vendor/go-simpler.org/sloglint/message_checks.go new file mode 100644 index 00000000..746745f0 --- /dev/null +++ b/vendor/go-simpler.org/sloglint/message_checks.go @@ -0,0 +1,81 @@ +package sloglint + +import ( + "go/ast" + "go/token" + "go/types" + "strconv" + "strings" + "unicode" + + "golang.org/x/tools/go/analysis" +) + +func staticMessage(pass *analysis.Pass, msg ast.Expr) { + var isStatic func(msg ast.Expr) bool + isStatic = func(msg ast.Expr) bool { + switch msg := msg.(type) { + case *ast.BasicLit: // e.g. slog.Info("msg") + return msg.Kind == token.STRING + case *ast.Ident: // e.g. slog.Info(constMsg) + _, isConst := pass.TypesInfo.ObjectOf(msg).(*types.Const) + return isConst + case *ast.BinaryExpr: // e.g. slog.Info("x" + "y") + if msg.Op != token.ADD { + panic("unreachable") // Only "+" can be applied to strings. + } + return isStatic(msg.X) && isStatic(msg.Y) + default: + return false + } + } + + if !isStatic(msg) { + pass.ReportRangef(msg, "message should be a string literal or a constant") + } +} + +func messageStyle(pass *analysis.Pass, msg ast.Expr, style string) { + lit, ok := msg.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return + } + + s, err := strconv.Unquote(lit.Value) + if err != nil { + panic("unreachable") // String literals are always quoted. + } + + runes := []rune(strings.TrimSpace(s)) + if len(runes) < 2 { + return + } + + first, second := runes[0], runes[1] + + if !unicode.IsLetter(first) { + return // e.g. "200 OK" + } + + switch style { + case messageStyleLowercased: + if unicode.IsLower(first) { + return + } + if unicode.IsPunct(second) { + return // e.g. "U.S." + } + if unicode.IsUpper(second) { + return // e.g. "HTTP" + } + case messageStyleCapitalized: + if unicode.IsUpper(first) { + return + } + if unicode.IsUpper(second) { + return // e.g. "iPhone" + } + } + + pass.ReportRangef(msg, "message should be %s", style) +} diff --git a/vendor/go-simpler.org/sloglint/options.go b/vendor/go-simpler.org/sloglint/options.go new file mode 100644 index 00000000..361eb593 --- /dev/null +++ b/vendor/go-simpler.org/sloglint/options.go @@ -0,0 +1,152 @@ +package sloglint + +import ( + "errors" + "flag" + "fmt" + "strings" +) + +// Func describes a function to analyze, e.g. [slog.Info]. +type Func struct { + // The full name of the function, including the package, e.g. "log/slog.Info". + // If the function is a method, the receiver type must be wrapped in parentheses, e.g. "(*log/slog.Logger).Info". + FullName string + // The position of the "msg string" argument in the function signature, starting from 0. + // If there is no message in the function, a negative value must be passed. + MessagePos int + // The position of the "args ...any" argument in the function signature, starting from 0. + // If there are no arguments in the function, a negative value must be passed. + ArgumentsPos int +} + +// Options contains options for the sloglint analyzer. +type Options struct { + // Report the use of global loggers ("all" or "default"). + NoGlobalLogger string + // Report the use of functions without a [context.Context] ("all" or "scope"). + ContextOnly string + + // Report dynamic log messages, such as those that are built with [fmt.Sprintf]. + StaticMessage bool + // Report log messages that do not match a particular style ("lowercased" or "capitalized"). + MessageStyle string + + // Report the use of both key-value pairs and attributes within a single function call (default true). + NoMixedArguments bool + // Report any use of attributes as function call arguments. + KeyValuePairsOnly bool + // Report any use of key-value pairs as function call arguments. + AttributesOnly bool + // Report two or more arguments on the same line. + ArgumentsOnSeparateLines bool + + // Report the use of string literals as log keys. + ConstantKeys bool + // Report the use of log keys that are not explicitly allowed. + AllowedKeys []string + // Report the use of forbidden log keys. + ForbiddenKeys []string + // Report log keys that do not match a particular naming case ("snake", "kebab", "camel", or "pascal"). + KeyNamingCase string + + // Analyze custom functions in addition to the standard [log/slog] functions. + CustomFuncs []Func +} + +// Possible values for [Options.NoGlobalLogger]. +const ( + noGlobalLoggerAll = "all" + noGlobalLoggerDefault = "default" +) + +// Possible values for [Options.ContextOnly]. +const ( + contextOnlyAll = "all" + contextOnlyScope = "scope" +) + +// Possible values for [Options.MessageStyle]. +const ( + messageStyleLowercased = "lowercased" + messageStyleCapitalized = "capitalized" +) + +// Possible values for [Options.KeyNamingCase]. +const ( + keyNamingCaseSnake = "snake" + keyNamingCaseKebab = "kebab" + keyNamingCaseCamel = "camel" + keyNamingCasePascal = "pascal" +) + +var ( + errIncompatible = errors.New("incompatible") + errInvalidValue = errors.New("invalid value") +) + +func (opts *Options) validate() error { + switch opts.NoGlobalLogger { + case "", noGlobalLoggerAll, noGlobalLoggerDefault: + default: + return fmt.Errorf("sloglint: Options.NoGlobalLogger has an %w %q", errInvalidValue, opts.NoGlobalLogger) + } + + switch opts.ContextOnly { + case "", contextOnlyAll, contextOnlyScope: + default: + return fmt.Errorf("sloglint: Options.ContextOnly has an %w %q", errInvalidValue, opts.ContextOnly) + } + + switch opts.MessageStyle { + case "", messageStyleLowercased, messageStyleCapitalized: + default: + return fmt.Errorf("sloglint: Options.MessageStyle has an %w %q", errInvalidValue, opts.MessageStyle) + } + + if opts.KeyValuePairsOnly && opts.AttributesOnly { + return fmt.Errorf("sloglint: Options.KeyValuePairsOnly and Options.AttributesOnly are %w", errIncompatible) + } + + switch opts.KeyNamingCase { + case "", keyNamingCaseSnake, keyNamingCaseKebab, keyNamingCaseCamel, keyNamingCasePascal: + default: + return fmt.Errorf("sloglint: Options.KeyNamingCase has an %w %q", errInvalidValue, opts.KeyNamingCase) + } + + return nil +} + +func flags(opts *Options) flag.FlagSet { + fs := flag.NewFlagSet("sloglint", flag.ContinueOnError) + + listVar := func(p *[]string, name, usage string) { + fs.Func(name, usage+" (comma-separated)", func(s string) error { + *p = append(*p, strings.Split(s, ",")...) + return nil + }) + } + + fs.StringVar(&opts.NoGlobalLogger, "no-global", opts.NoGlobalLogger, `report the use of global loggers ("all" or "default")`) + fs.StringVar(&opts.ContextOnly, "ctx-only", opts.ContextOnly, `report the use of functions without a context.Context ("all" or "scope")`) + fs.BoolVar(&opts.StaticMessage, "static-msg", opts.StaticMessage, `report dynamic log messages, such as those that are built with fmt.Sprintf`) + fs.StringVar(&opts.MessageStyle, "msg-style", opts.MessageStyle, `report log messages that do not match a particular style ("lowercased" or "capitalized")`) + fs.BoolVar(&opts.NoMixedArguments, "no-mixed-args", opts.NoMixedArguments, `report the use of both key-value pairs and attributes within a single function call (default true)`) + fs.BoolVar(&opts.KeyValuePairsOnly, "kv-only", opts.KeyValuePairsOnly, `report any use of attributes as function call arguments`) + fs.BoolVar(&opts.AttributesOnly, "attr-only", opts.AttributesOnly, `report any use of key-value pairs as function call arguments`) + fs.BoolVar(&opts.ArgumentsOnSeparateLines, "args-on-sep-lines", opts.ArgumentsOnSeparateLines, `report two or more arguments on the same line`) + fs.BoolVar(&opts.ConstantKeys, "const-keys", opts.ConstantKeys, `report the use of string literal as log keys`) + listVar(&opts.AllowedKeys, "allowed-keys", `report the use of log keys that are not explicitly allowed`) + listVar(&opts.ForbiddenKeys, "forbidden-keys", `report the use of forbidden log keys`) + fs.StringVar(&opts.KeyNamingCase, "key-naming-case", opts.KeyNamingCase, `report log keys that do not match a particular naming case ("snake", "kebab", "camel", or "pascal")`) + + fs.Func("fn", `analyze a custom function (format: "full-name:msg-pos:args-pos")`, func(s string) error { + name, rest, _ := strings.Cut(s, ":") + fn := Func{FullName: name} + _, err := fmt.Sscanf(rest, "%d:%d", &fn.MessagePos, &fn.ArgumentsPos) + opts.CustomFuncs = append(opts.CustomFuncs, fn) + return err + }) + + return *fs +} diff --git a/vendor/go-simpler.org/sloglint/sloglint.go b/vendor/go-simpler.org/sloglint/sloglint.go deleted file mode 100644 index ff5d1f80..00000000 --- a/vendor/go-simpler.org/sloglint/sloglint.go +++ /dev/null @@ -1,576 +0,0 @@ -// Package sloglint implements the sloglint analyzer. -package sloglint - -import ( - "errors" - "flag" - "fmt" - "go/ast" - "go/token" - "go/types" - "go/version" - "iter" - "slices" - "strconv" - "strings" - "unicode" - - "github.com/ettle/strcase" - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/inspector" - "golang.org/x/tools/go/types/typeutil" -) - -// Options are options for the sloglint analyzer. -type Options struct { - NoMixedArgs bool // Enforce not mixing key-value pairs and attributes (default true). - KVOnly bool // Enforce using key-value pairs only (overrides NoMixedArgs, incompatible with AttrOnly). - AttrOnly bool // Enforce using attributes only (overrides NoMixedArgs, incompatible with KVOnly). - NoGlobal string // Enforce not using global loggers ("all" or "default"). - ContextOnly string // Enforce using methods that accept a context ("all" or "scope"). - StaticMsg bool // Enforce using static messages. - MsgStyle string // Enforce message style ("lowercased" or "capitalized"). - NoRawKeys bool // Enforce using constants instead of raw keys. - KeyNamingCase string // Enforce key naming convention ("snake", "kebab", "camel", or "pascal"). - ForbiddenKeys []string // Enforce not using specific keys. - ArgsOnSepLines bool // Enforce putting arguments on separate lines. - - go124 bool -} - -// New creates a new sloglint analyzer. -func New(opts *Options) *analysis.Analyzer { - if opts == nil { - opts = &Options{NoMixedArgs: true} - } - - return &analysis.Analyzer{ - Name: "sloglint", - Doc: "ensure consistent code style when using log/slog", - Flags: flags(opts), - Requires: []*analysis.Analyzer{inspect.Analyzer}, - Run: func(pass *analysis.Pass) (any, error) { - if opts.KVOnly && opts.AttrOnly { - return nil, fmt.Errorf("sloglint: Options.KVOnly and Options.AttrOnly: %w", errIncompatible) - } - - switch opts.NoGlobal { - case "", "all", "default": - default: - return nil, fmt.Errorf("sloglint: Options.NoGlobal=%s: %w", opts.NoGlobal, errInvalidValue) - } - - switch opts.ContextOnly { - case "", "all", "scope": - default: - return nil, fmt.Errorf("sloglint: Options.ContextOnly=%s: %w", opts.ContextOnly, errInvalidValue) - } - - switch opts.MsgStyle { - case "", styleLowercased, styleCapitalized: - default: - return nil, fmt.Errorf("sloglint: Options.MsgStyle=%s: %w", opts.MsgStyle, errInvalidValue) - } - - switch opts.KeyNamingCase { - case "", snakeCase, kebabCase, camelCase, pascalCase: - default: - return nil, fmt.Errorf("sloglint: Options.KeyNamingCase=%s: %w", opts.KeyNamingCase, errInvalidValue) - } - - if version.Compare("go"+pass.Module.GoVersion, "go1.24") >= 0 { - opts.go124 = true - } - - run(pass, opts) - return nil, nil - }, - } -} - -var ( - errIncompatible = errors.New("incompatible options") - errInvalidValue = errors.New("invalid value") -) - -func flags(opts *Options) flag.FlagSet { - fset := flag.NewFlagSet("sloglint", flag.ContinueOnError) - - boolVar := func(value *bool, name, usage string) { - fset.Func(name, usage, func(s string) error { - v, err := strconv.ParseBool(s) - *value = v - return err - }) - } - - strVar := func(value *string, name, usage string) { - fset.Func(name, usage, func(s string) error { - *value = s - return nil - }) - } - - boolVar(&opts.NoMixedArgs, "no-mixed-args", "enforce not mixing key-value pairs and attributes (default true)") - boolVar(&opts.KVOnly, "kv-only", "enforce using key-value pairs only (overrides -no-mixed-args, incompatible with -attr-only)") - boolVar(&opts.AttrOnly, "attr-only", "enforce using attributes only (overrides -no-mixed-args, incompatible with -kv-only)") - strVar(&opts.NoGlobal, "no-global", "enforce not using global loggers (all|default)") - strVar(&opts.ContextOnly, "context-only", "enforce using methods that accept a context (all|scope)") - boolVar(&opts.StaticMsg, "static-msg", "enforce using static messages") - strVar(&opts.MsgStyle, "msg-style", "enforce message style (lowercased|capitalized)") - boolVar(&opts.NoRawKeys, "no-raw-keys", "enforce using constants instead of raw keys") - strVar(&opts.KeyNamingCase, "key-naming-case", "enforce key naming convention (snake|kebab|camel|pascal)") - boolVar(&opts.ArgsOnSepLines, "args-on-sep-lines", "enforce putting arguments on separate lines") - - fset.Func("forbidden-keys", "enforce not using specific keys (comma-separated)", func(s string) error { - opts.ForbiddenKeys = append(opts.ForbiddenKeys, strings.Split(s, ",")...) - return nil - }) - - return *fset -} - -var slogFuncs = map[string]struct { - argsPos int - skipContextCheck bool -}{ - "log/slog.With": {argsPos: 0, skipContextCheck: true}, - "log/slog.Log": {argsPos: 3}, - "log/slog.LogAttrs": {argsPos: 3}, - "log/slog.Debug": {argsPos: 1}, - "log/slog.Info": {argsPos: 1}, - "log/slog.Warn": {argsPos: 1}, - "log/slog.Error": {argsPos: 1}, - "log/slog.DebugContext": {argsPos: 2}, - "log/slog.InfoContext": {argsPos: 2}, - "log/slog.WarnContext": {argsPos: 2}, - "log/slog.ErrorContext": {argsPos: 2}, - "(*log/slog.Logger).With": {argsPos: 0, skipContextCheck: true}, - "(*log/slog.Logger).Log": {argsPos: 3}, - "(*log/slog.Logger).LogAttrs": {argsPos: 3}, - "(*log/slog.Logger).Debug": {argsPos: 1}, - "(*log/slog.Logger).Info": {argsPos: 1}, - "(*log/slog.Logger).Warn": {argsPos: 1}, - "(*log/slog.Logger).Error": {argsPos: 1}, - "(*log/slog.Logger).DebugContext": {argsPos: 2}, - "(*log/slog.Logger).InfoContext": {argsPos: 2}, - "(*log/slog.Logger).WarnContext": {argsPos: 2}, - "(*log/slog.Logger).ErrorContext": {argsPos: 2}, -} - -var attrFuncs = map[string]struct{}{ - "log/slog.String": {}, - "log/slog.Int64": {}, - "log/slog.Int": {}, - "log/slog.Uint64": {}, - "log/slog.Float64": {}, - "log/slog.Bool": {}, - "log/slog.Time": {}, - "log/slog.Duration": {}, - "log/slog.Group": {}, - "log/slog.Any": {}, -} - -// message styles. -const ( - styleLowercased = "lowercased" - styleCapitalized = "capitalized" -) - -// key naming conventions. -const ( - snakeCase = "snake" - kebabCase = "kebab" - camelCase = "camel" - pascalCase = "pascal" -) - -func run(pass *analysis.Pass, opts *Options) { - visitor := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - filter := []ast.Node{(*ast.CallExpr)(nil)} - - // WithStack is ~2x slower than Preorder, use it only when stack is needed. - if opts.ContextOnly == "scope" { - visitor.WithStack(filter, func(node ast.Node, _ bool, stack []ast.Node) bool { - visit(pass, opts, node, stack) - return false - }) - return - } - - visitor.Preorder(filter, func(node ast.Node) { - visit(pass, opts, node, nil) - }) -} - -// NOTE: stack is nil if Preorder is used. -func visit(pass *analysis.Pass, opts *Options, node ast.Node, stack []ast.Node) { - call := node.(*ast.CallExpr) - - fn := typeutil.StaticCallee(pass.TypesInfo, call) - if fn == nil { - return - } - - name := fn.FullName() - - checkDiscardHandler(opts, pass, name, call) - - funcInfo, ok := slogFuncs[name] - if !ok { - return - } - - switch opts.NoGlobal { - case "all": - if strings.HasPrefix(name, "log/slog.") || isGlobalLoggerUsed(pass.TypesInfo, call.Fun) { - pass.Reportf(call.Pos(), "global logger should not be used") - } - case "default": - if strings.HasPrefix(name, "log/slog.") { - pass.Reportf(call.Pos(), "default logger should not be used") - } - } - - // NOTE: "With" functions are not checked for context.Context. - if !funcInfo.skipContextCheck { - switch opts.ContextOnly { - case "all": - typ := pass.TypesInfo.TypeOf(call.Args[0]) - if typ != nil && typ.String() != "context.Context" { - pass.Reportf(call.Pos(), "%sContext should be used instead", fn.Name()) - } - case "scope": - typ := pass.TypesInfo.TypeOf(call.Args[0]) - if typ != nil && typ.String() != "context.Context" && isContextInScope(pass.TypesInfo, stack) { - pass.Reportf(call.Pos(), "%sContext should be used instead", fn.Name()) - } - } - } - - msgPos := funcInfo.argsPos - 1 - - // NOTE: "With" functions have no message argument and must be skipped. - if opts.StaticMsg && msgPos >= 0 && !isStaticMsg(pass.TypesInfo, call.Args[msgPos]) { - pass.Reportf(call.Args[msgPos].Pos(), "message should be a string literal or a constant") - } - - if opts.MsgStyle != "" && msgPos >= 0 { - if lit, ok := call.Args[msgPos].(*ast.BasicLit); ok && lit.Kind == token.STRING { - value, err := strconv.Unquote(lit.Value) - if err != nil { - panic("unreachable") // string literals are always quoted. - } - if ok := isValidMsgStyle(value, opts.MsgStyle); !ok { - pass.Reportf(call.Args[msgPos].Pos(), "message should be %s", opts.MsgStyle) - } - } - } - - // NOTE: we assume that the arguments have already been validated by govet. - args := call.Args[funcInfo.argsPos:] - if len(args) == 0 { - return - } - - var keys []ast.Expr - var attrs []ast.Expr - - for i := 0; i < len(args); i++ { - typ := pass.TypesInfo.TypeOf(args[i]) - if typ == nil { - continue - } - switch typ.String() { - case "string": - keys = append(keys, args[i]) - i++ // skip the value. - case "log/slog.Attr": - attrs = append(attrs, args[i]) - case "[]any", "[]log/slog.Attr": - continue // the last argument may be an unpacked slice, skip it. - } - } - - switch { - case opts.KVOnly && len(attrs) > 0: - pass.Reportf(call.Pos(), "attributes should not be used") - case opts.AttrOnly && len(keys) > 0: - pass.Reportf(call.Pos(), "key-value pairs should not be used") - case opts.NoMixedArgs && len(attrs) > 0 && len(keys) > 0: - pass.Reportf(call.Pos(), "key-value pairs and attributes should not be mixed") - } - - if opts.NoRawKeys { - for key := range AllKeys(pass.TypesInfo, keys, attrs) { - if sel, ok := key.(*ast.SelectorExpr); ok { - key = sel.Sel // the key is defined in another package, e.g. pkg.ConstKey. - } - - isConst := false - - if ident, ok := key.(*ast.Ident); ok { - if obj := pass.TypesInfo.ObjectOf(ident); obj != nil { - if _, ok := obj.(*types.Const); ok { - isConst = true - } - } - } - - if !isConst { - pass.Reportf(key.Pos(), "raw keys should not be used") - } - } - } - - checkKeysNaming(opts, pass, keys, attrs) - - if len(opts.ForbiddenKeys) > 0 { - for key := range AllKeys(pass.TypesInfo, keys, attrs) { - if name, ok := getKeyName(key); ok && slices.Contains(opts.ForbiddenKeys, name) { - pass.Reportf(key.Pos(), "%q key is forbidden and should not be used", name) - } - } - } - - if opts.ArgsOnSepLines && areArgsOnSameLine(pass.Fset, call, keys, attrs) { - pass.Reportf(call.Pos(), "arguments should be put on separate lines") - } -} - -func checkKeysNaming(opts *Options, pass *analysis.Pass, keys, attrs []ast.Expr) { - checkKeyNamingCase := func(caseFn func(string) string, caseName string) { - for key := range AllKeys(pass.TypesInfo, keys, attrs) { - name, ok := getKeyName(key) - if !ok || name == caseFn(name) { - return - } - - pass.Report(analysis.Diagnostic{ - Pos: key.Pos(), - Message: fmt.Sprintf("keys should be written in %s", caseName), - SuggestedFixes: []analysis.SuggestedFix{{ - TextEdits: []analysis.TextEdit{{ - Pos: key.Pos(), - End: key.End(), - NewText: []byte(strconv.Quote(caseFn(name))), - }}, - }}, - }) - } - } - - switch opts.KeyNamingCase { - case snakeCase: - checkKeyNamingCase(strcase.ToSnake, "snake_case") - case kebabCase: - checkKeyNamingCase(strcase.ToKebab, "kebab-case") - case camelCase: - checkKeyNamingCase(strcase.ToCamel, "camelCase") - case pascalCase: - checkKeyNamingCase(strcase.ToPascal, "PascalCase") - } -} - -func checkDiscardHandler(opts *Options, pass *analysis.Pass, name string, call *ast.CallExpr) { - if !opts.go124 { - return - } - - if name != "log/slog.NewTextHandler" && name != "log/slog.NewJSONHandler" { - return - } - - sel, ok := call.Args[0].(*ast.SelectorExpr) - if !ok { - return - } - - obj := pass.TypesInfo.ObjectOf(sel.Sel) - if obj == nil { - return - } - - if obj.Pkg().Name() != "io" || obj.Name() != "Discard" { - return - } - - pass.Report(analysis.Diagnostic{ - Pos: call.Pos(), - Message: "use slog.DiscardHandler instead", - SuggestedFixes: []analysis.SuggestedFix{{ - TextEdits: []analysis.TextEdit{{ - Pos: call.Pos(), - End: call.End(), - NewText: []byte("slog.DiscardHandler"), - }}, - }}, - }) -} - -func isGlobalLoggerUsed(info *types.Info, call ast.Expr) bool { - sel, ok := call.(*ast.SelectorExpr) - if !ok { - return false - } - ident, ok := sel.X.(*ast.Ident) - if !ok { - return false - } - obj := info.ObjectOf(ident) - return obj.Parent() == obj.Pkg().Scope() -} - -func isContextInScope(info *types.Info, stack []ast.Node) bool { - for i := len(stack) - 1; i >= 0; i-- { - decl, ok := stack[i].(*ast.FuncDecl) - if !ok { - continue - } - params := decl.Type.Params - if len(params.List) == 0 || len(params.List[0].Names) == 0 { - continue - } - typ := info.TypeOf(params.List[0].Names[0]) - if typ != nil && typ.String() == "context.Context" { - return true - } - } - return false -} - -func isStaticMsg(info *types.Info, msg ast.Expr) bool { - switch msg := msg.(type) { - case *ast.BasicLit: // e.g. slog.Info("msg") - return msg.Kind == token.STRING - case *ast.Ident: // e.g. const msg = "msg"; slog.Info(msg) - _, isConst := info.ObjectOf(msg).(*types.Const) - return isConst - case *ast.BinaryExpr: // e.g. slog.Info("x" + "y") - if msg.Op != token.ADD { - panic("unreachable") // only + can be applied to strings. - } - return isStaticMsg(info, msg.X) && isStaticMsg(info, msg.Y) - default: - return false - } -} - -func isValidMsgStyle(msg, style string) bool { - runes := []rune(msg) - if len(runes) < 2 { - return true - } - - first, second := runes[0], runes[1] - - switch style { - case styleLowercased: - if unicode.IsLower(first) { - return true - } - if unicode.IsPunct(second) { - return true // e.g. "U.S.A." - } - return unicode.IsUpper(second) // e.g. "HTTP" - case styleCapitalized: - if unicode.IsUpper(first) { - return true - } - return unicode.IsUpper(second) // e.g. "iPhone" - default: - panic("unreachable") - } -} - -func AllKeys(info *types.Info, keys, attrs []ast.Expr) iter.Seq[ast.Expr] { - return func(yield func(key ast.Expr) bool) { - for _, key := range keys { - if !yield(key) { - return - } - } - - for _, attr := range attrs { - switch attr := attr.(type) { - case *ast.CallExpr: // e.g. slog.Int() - callee := typeutil.StaticCallee(info, attr) - if callee == nil { - continue - } - if _, ok := attrFuncs[callee.FullName()]; !ok { - continue - } - - if !yield(attr.Args[0]) { - return - } - - case *ast.CompositeLit: // slog.Attr{} - switch len(attr.Elts) { - case 1: // slog.Attr{Key: ...} | slog.Attr{Value: ...} - if kv := attr.Elts[0].(*ast.KeyValueExpr); kv.Key.(*ast.Ident).Name == "Key" { - if !yield(kv.Value) { - return - } - } - - case 2: // slog.Attr{Key: ..., Value: ...} | slog.Attr{Value: ..., Key: ...} | slog.Attr{..., ...} - if kv, ok := attr.Elts[0].(*ast.KeyValueExpr); ok && kv.Key.(*ast.Ident).Name == "Key" { - if !yield(kv.Value) { - return - } - } else if kv, ok := attr.Elts[1].(*ast.KeyValueExpr); ok && kv.Key.(*ast.Ident).Name == "Key" { - if !yield(kv.Value) { - return - } - } else { - if !yield(attr.Elts[0]) { - return - } - } - } - } - } - } -} - -func getKeyName(key ast.Expr) (string, bool) { - if ident, ok := key.(*ast.Ident); ok { - if ident.Obj == nil || ident.Obj.Decl == nil || ident.Obj.Kind != ast.Con { - return "", false - } - if spec, ok := ident.Obj.Decl.(*ast.ValueSpec); ok && len(spec.Values) > 0 { - // TODO: support len(spec.Values) > 1; e.g. const foo, bar = 1, 2 - key = spec.Values[0] - } - } - if lit, ok := key.(*ast.BasicLit); ok && lit.Kind == token.STRING { - value, err := strconv.Unquote(lit.Value) - if err != nil { - panic("unreachable") // string literals are always quoted. - } - return value, true - } - return "", false -} - -func areArgsOnSameLine(fset *token.FileSet, call ast.Expr, keys, attrs []ast.Expr) bool { - if len(keys)+len(attrs) <= 1 { - return false // special case: slog.Info("msg", "key", "value") is ok. - } - - args := slices.Concat([]ast.Expr{call}, keys, attrs) - - lines := make(map[int]struct{}, len(args)) - for _, arg := range args { - line := fset.Position(arg.Pos()).Line - if _, ok := lines[line]; ok { - return true - } - lines[line] = struct{}{} - } - - return false -} diff --git a/vendor/go-simpler.org/sloglint/utils.go b/vendor/go-simpler.org/sloglint/utils.go new file mode 100644 index 00000000..7e86e772 --- /dev/null +++ b/vendor/go-simpler.org/sloglint/utils.go @@ -0,0 +1,47 @@ +package sloglint + +import ( + "go/ast" + "go/token" + "go/types" + "strconv" + + "golang.org/x/tools/go/types/typeutil" +) + +func typeName(info *types.Info, expr ast.Expr) string { + if typ := info.TypeOf(expr); typ != nil { + return typ.String() + } + return "" +} + +func funcName(info *types.Info, call *ast.CallExpr) string { + if fn := typeutil.StaticCallee(info, call); fn != nil { + return fn.FullName() + } + return "" +} + +func keyName(key ast.Expr) (string, bool) { + if ident, ok := key.(*ast.Ident); ok { + if ident.Obj == nil || ident.Obj.Decl == nil || ident.Obj.Kind != ast.Con { + return "", false + } + if spec, ok := ident.Obj.Decl.(*ast.ValueSpec); ok && len(spec.Values) > 0 { + key = spec.Values[0] // TODO: Support len(spec.Values) > 1; e.g. const foo, bar = 1, 2. + } + } + + lit, ok := key.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return "", false + } + + name, err := strconv.Unquote(lit.Value) + if err != nil { + panic("unreachable") // String literals are always quoted. + } + + return name, true +} diff --git a/vendor/go.augendre.info/fatcontext/pkg/analyzer/analyzer.go b/vendor/go.augendre.info/fatcontext/pkg/analyzer/analyzer.go index 88c75bdb..82972e42 100644 --- a/vendor/go.augendre.info/fatcontext/pkg/analyzer/analyzer.go +++ b/vendor/go.augendre.info/fatcontext/pkg/analyzer/analyzer.go @@ -15,9 +15,13 @@ import ( "golang.org/x/tools/go/ast/inspector" ) -// FlagCheckStructPointers is a possible flag for the analyzer. -// Exported to make it usable in golangci-lint. -const FlagCheckStructPointers = "check-struct-pointers" +// Flags for the analyzer. +// Exported to make them usable in golangci-lint. +const ( + FlagCheckStructPointers = "check-struct-pointers" + FlagCheckLoops = "check-loops" + FlagCheckFunctionLiterals = "check-function-literals" +) // NewAnalyzer returns a fatcontext analyzer. func NewAnalyzer() *analysis.Analyzer { @@ -26,6 +30,10 @@ func NewAnalyzer() *analysis.Analyzer { flags := flag.NewFlagSet("fatcontext", flag.ExitOnError) flags.BoolVar(&rnnr.DetectInStructPointers, FlagCheckStructPointers, false, "set to true to detect potential fat contexts in struct pointers") + flags.BoolVar(&rnnr.CheckLoops, FlagCheckLoops, true, + "set to false to disable detection of fat contexts in loops") + flags.BoolVar(&rnnr.CheckFunctionLiterals, FlagCheckFunctionLiterals, true, + "set to false to disable detection of fat contexts in function literals") return &analysis.Analyzer{ Name: "fatcontext", @@ -50,9 +58,11 @@ const ( type runner struct { DetectInStructPointers bool + CheckLoops bool + CheckFunctionLiterals bool } -func (r *runner) run(pass *analysis.Pass) (interface{}, error) { +func (r *runner) run(pass *analysis.Pass) (any, error) { inspctr, typeValid := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) if !typeValid { return nil, errInvalidAnalysis @@ -99,7 +109,16 @@ func (r *runner) run(pass *analysis.Pass) (interface{}, error) { } func (r *runner) shouldIgnoreReport(category string) bool { - return category == categoryInStructPointer && !r.DetectInStructPointers + switch category { + case categoryInLoop: + return !r.CheckLoops + case categoryInFuncLit: + return !r.CheckFunctionLiterals + case categoryInStructPointer: + return !r.DetectInStructPointers + } + + return false } func (r *runner) getSuggestedFixes( @@ -268,7 +287,7 @@ func getStmtList(stmt ast.Stmt) []ast.Stmt { } // render returns the pretty-print of the given node. -func render(fset *token.FileSet, x interface{}) ([]byte, error) { +func render(fset *token.FileSet, x any) ([]byte, error) { var buf bytes.Buffer err := printer.Fprint(&buf, fset, x) diff --git a/vendor/go.yaml.in/yaml/v3/parserc.go b/vendor/go.yaml.in/yaml/v3/parserc.go index 25fe8236..f35829db 100644 --- a/vendor/go.yaml.in/yaml/v3/parserc.go +++ b/vendor/go.yaml.in/yaml/v3/parserc.go @@ -226,9 +226,9 @@ func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool } // Parse the production: -// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END // -// ************ +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// ************ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -249,13 +249,11 @@ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) } // Parse the productions: -// implicit_document ::= block_node DOCUMENT-END* -// -// * // -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -// -// ************************* +// implicit_document ::= block_node DOCUMENT-END* +// * +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// ************************* func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { token := peek_token(parser) @@ -359,9 +357,9 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t } // Parse the productions: -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // -// *********** +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// *********** func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -382,11 +380,10 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event } // Parse the productions: -// implicit_document ::= block_node DOCUMENT-END* -// -// ************* // -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// implicit_document ::= block_node DOCUMENT-END* +// ************* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -432,42 +429,32 @@ func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) } // Parse the productions: -// block_node_or_indentless_sequence ::= -// -// ALIAS -// ***** -// | properties (block_content | indentless_block_sequence)? -// ********** * -// | block_content | indentless_block_sequence -// * -// -// block_node ::= ALIAS -// -// ***** -// | properties block_content? -// ********** * -// | block_content -// * -// -// flow_node ::= ALIAS -// -// ***** -// | properties flow_content? -// ********** * -// | flow_content -// * -// -// properties ::= TAG ANCHOR? | ANCHOR TAG? -// -// ************************* -// -// block_content ::= block_collection | flow_collection | SCALAR -// -// ****** // -// flow_content ::= flow_collection | SCALAR -// -// ****** +// block_node_or_indentless_sequence ::= +// ALIAS +// ***** +// | properties (block_content | indentless_block_sequence)? +// ********** * +// | block_content | indentless_block_sequence +// * +// block_node ::= ALIAS +// ***** +// | properties block_content? +// ********** * +// | block_content +// * +// flow_node ::= ALIAS +// ***** +// | properties flow_content? +// ********** * +// | flow_content +// * +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// ************************* +// block_content ::= block_collection | flow_collection | SCALAR +// ****** +// flow_content ::= flow_collection | SCALAR +// ****** func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() @@ -697,9 +684,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i } // Parse the productions: -// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END // -// ******************** *********** * ********* +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// ******************** *********** * ********* func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -755,9 +742,9 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e } // Parse the productions: -// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ // -// *********** * +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// *********** * func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -821,15 +808,15 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { } // Parse the productions: -// block_mapping ::= BLOCK-MAPPING_START // -// ******************* -// ((KEY block_node_or_indentless_sequence?)? -// *** * -// (VALUE block_node_or_indentless_sequence?)?)* +// block_mapping ::= BLOCK-MAPPING_START +// ******************* +// ((KEY block_node_or_indentless_sequence?)? +// *** * +// (VALUE block_node_or_indentless_sequence?)?)* // -// BLOCK-END -// ********* +// BLOCK-END +// ********* func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -896,13 +883,14 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even } // Parse the productions: -// block_mapping ::= BLOCK-MAPPING_START // -// ((KEY block_node_or_indentless_sequence?)? +// block_mapping ::= BLOCK-MAPPING_START +// +// ((KEY block_node_or_indentless_sequence?)? // -// (VALUE block_node_or_indentless_sequence?)?)* -// ***** * -// BLOCK-END +// (VALUE block_node_or_indentless_sequence?)?)* +// ***** * +// BLOCK-END func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -929,19 +917,17 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev } // Parse the productions: -// flow_sequence ::= FLOW-SEQUENCE-START -// -// ******************* -// (flow_sequence_entry FLOW-ENTRY)* -// * ********** -// flow_sequence_entry? -// * -// FLOW-SEQUENCE-END -// ***************** // -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// -// * +// flow_sequence ::= FLOW-SEQUENCE-START +// ******************* +// (flow_sequence_entry FLOW-ENTRY)* +// * ********** +// flow_sequence_entry? +// * +// FLOW-SEQUENCE-END +// ***************** +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -1005,9 +991,9 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev } // Parse the productions: -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // -// *** * +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// *** * func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -1026,9 +1012,9 @@ func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, ev } // Parse the productions: -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // -// ***** * +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// ***** * func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -1050,9 +1036,9 @@ func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, } // Parse the productions: -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // -// * +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -1068,18 +1054,17 @@ func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, ev } // Parse the productions: -// flow_mapping ::= FLOW-MAPPING-START -// -// ****************** -// (flow_mapping_entry FLOW-ENTRY)* -// * ********** -// flow_mapping_entry? -// ****************** -// FLOW-MAPPING-END -// **************** // -// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// - *** * +// flow_mapping ::= FLOW-MAPPING-START +// ****************** +// (flow_mapping_entry FLOW-ENTRY)* +// * ********** +// flow_mapping_entry? +// ****************** +// FLOW-MAPPING-END +// **************** +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * *** * func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -1144,8 +1129,9 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event } // Parse the productions: -// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// - ***** * +// +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * ***** * func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { token := peek_token(parser) if token == nil { diff --git a/vendor/go.yaml.in/yaml/v3/yamlh.go b/vendor/go.yaml.in/yaml/v3/yamlh.go index f59aa40f..07c44236 100644 --- a/vendor/go.yaml.in/yaml/v3/yamlh.go +++ b/vendor/go.yaml.in/yaml/v3/yamlh.go @@ -433,21 +433,19 @@ type yaml_document_t struct { // The prototype of a read handler. // -// The read handler is called when the parser needs to read more bytes from the -// source. The handler should write not more than size bytes to the buffer. -// The number of written bytes should be set to the size_read variable. +// The read handler is called when the parser needs to read more bytes from the +// source. The handler should write not more than size bytes to the buffer. +// The number of written bytes should be set to the size_read variable. // -// [in,out] data A pointer to an application data specified by +// [in,out] data A pointer to an application data specified by +// yaml_parser_set_input(). +// [out] buffer The buffer to write the data from the source. +// [in] size The size of the buffer. +// [out] size_read The actual number of bytes read from the source. // -// yaml_parser_set_input(). -// -// [out] buffer The buffer to write the data from the source. -// [in] size The size of the buffer. -// [out] size_read The actual number of bytes read from the source. -// -// On success, the handler should return 1. If the handler failed, -// the returned value should be 0. On EOF, the handler should set the -// size_read to 0 and return 1. +// On success, the handler should return 1. If the handler failed, +// the returned value should be 0. On EOF, the handler should set the +// size_read to 0 and return 1. type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) // This structure holds information about a potential simple key. @@ -655,19 +653,17 @@ type yaml_comment_t struct { // The prototype of a write handler. // -// The write handler is called when the emitter needs to flush the accumulated -// characters to the output. The handler should write @a size bytes of the -// @a buffer to the output. -// -// @param[in,out] data A pointer to an application data specified by -// -// yaml_emitter_set_output(). +// The write handler is called when the emitter needs to flush the accumulated +// characters to the output. The handler should write @a size bytes of the +// @a buffer to the output. // -// @param[in] buffer The buffer with bytes to be written. -// @param[in] size The size of the buffer. +// @param[in,out] data A pointer to an application data specified by +// yaml_emitter_set_output(). +// @param[in] buffer The buffer with bytes to be written. +// @param[in] size The size of the buffer. // -// @returns On success, the handler should return @c 1. If the handler failed, -// the returned value should be @c 0. +// @returns On success, the handler should return @c 1. If the handler failed, +// the returned value should be @c 0. type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error type yaml_emitter_state_t int diff --git a/vendor/golang.org/x/exp/typeparams/common.go b/vendor/golang.org/x/exp/typeparams/common.go index 820ca7bd..c2dd4a19 100644 --- a/vendor/golang.org/x/exp/typeparams/common.go +++ b/vendor/golang.org/x/exp/typeparams/common.go @@ -8,10 +8,7 @@ // Many of the types and functions in this package are proxies for the new APIs // introduced in the standard library with Go 1.18. For example, the // typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec -// function returns the value of the go/ast.TypeSpec.TypeParams field. At Go -// versions older than 1.18 these helpers are implemented as stubs, allowing -// users of this package to write code that handles generic constructs inline, -// even if the Go version being used to compile does not support generics. +// function returns the value of the go/ast.TypeSpec.TypeParams field. // // Additionally, this package contains common utilities for working with the // new generic constructs, to supplement the standard library APIs. Notably, @@ -28,8 +25,12 @@ import ( // Enabled reports whether type parameters are enabled in the current build // environment. +// +// Deprecated: Type parameters are always enabled. Or use go fix (after go1.26). +// +//go:fix inline func Enabled() bool { - return enabled + return true } // UnpackIndexExpr extracts data from AST nodes that represent index diff --git a/vendor/golang.org/x/exp/typeparams/normalize.go b/vendor/golang.org/x/exp/typeparams/normalize.go index 6cf71f04..57370173 100644 --- a/vendor/golang.org/x/exp/typeparams/normalize.go +++ b/vendor/golang.org/x/exp/typeparams/normalize.go @@ -192,9 +192,3 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in } return tset, nil } - -// under is a facade for the go/types internal function of the same name. It is -// used by typeterm.go. -func under(t types.Type) types.Type { - return t.Underlying() -} diff --git a/vendor/golang.org/x/exp/typeparams/termlist.go b/vendor/golang.org/x/exp/typeparams/termlist.go index 6f88bfa9..a59a0a72 100644 --- a/vendor/golang.org/x/exp/typeparams/termlist.go +++ b/vendor/golang.org/x/exp/typeparams/termlist.go @@ -1,14 +1,15 @@ +// Code generated by copytermlist.go DO NOT EDIT. +// Source: GOROOT/src/go/types/termlist.go + // Copyright 2021 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 copytermlist.go DO NOT EDIT. - package typeparams import ( - "bytes" "go/types" + "strings" ) // A termlist represents the type set represented by the union @@ -22,15 +23,18 @@ type termlist []*term // It is in normal form. var allTermlist = termlist{new(term)} +// termSep is the separator used between individual terms. +const termSep = " | " + // String prints the termlist exactly (without normalization). func (xl termlist) String() string { if len(xl) == 0 { return "∅" } - var buf bytes.Buffer + var buf strings.Builder for i, x := range xl { if i > 0 { - buf.WriteString(" ∪ ") + buf.WriteString(termSep) } buf.WriteString(x.String()) } @@ -97,15 +101,6 @@ func (xl termlist) norm() termlist { return rl } -// If the type set represented by xl is specified by a single (non-𝓤) term, -// singleType returns that type. Otherwise it returns nil. -func (xl termlist) singleType() types.Type { - if nl := xl.norm(); len(nl) == 1 { - return nl[0].typ // if nl.isAll() then typ is nil, which is ok - } - return nil -} - // union returns the union xl ∪ yl. func (xl termlist) union(yl termlist) termlist { return append(xl, yl...).norm() diff --git a/vendor/golang.org/x/exp/typeparams/typeparams_go118.go b/vendor/golang.org/x/exp/typeparams/typeparams.go similarity index 67% rename from vendor/golang.org/x/exp/typeparams/typeparams_go118.go rename to vendor/golang.org/x/exp/typeparams/typeparams.go index 0b35449d..1b3bc8c9 100644 --- a/vendor/golang.org/x/exp/typeparams/typeparams_go118.go +++ b/vendor/golang.org/x/exp/typeparams/typeparams.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.18 - package typeparams import ( @@ -11,9 +9,11 @@ import ( "go/types" ) -const enabled = true - // IndexListExpr is an alias for ast.IndexListExpr. +// +// Deprecated: Use [go/ast.IndexListExpr] instead. +// +//go:fix inline type IndexListExpr = ast.IndexListExpr // ForTypeSpec returns n.TypeParams. @@ -33,115 +33,215 @@ func ForFuncType(n *ast.FuncType) *ast.FieldList { } // TypeParam is an alias for types.TypeParam +// +// Deprecated: Use [go/types.TypeParam] instead. +// +//go:fix inline type TypeParam = types.TypeParam // TypeParamList is an alias for types.TypeParamList +// +// Deprecated: Use [go/types.TypeParamList] instead. +// +//go:fix inline type TypeParamList = types.TypeParamList // TypeList is an alias for types.TypeList +// +// Deprecated: Use [go/types.TypeList] instead. +// +//go:fix inline type TypeList = types.TypeList // NewTypeParam calls types.NewTypeParam. +// +// Deprecated: Use [go/types.NewTypeParam] instead. +// +//go:fix inline func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { return types.NewTypeParam(name, constraint) } // NewSignatureType calls types.NewSignatureType. +// +// Deprecated: Use [go/types.NewSignatureType] instead. +// +//go:fix inline func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { return types.NewSignatureType(recv, recvTypeParams, typeParams, params, results, variadic) } // ForSignature returns sig.TypeParams() +// +// Deprecated: Use sig.TypeParams() instead. +// +//go:fix inline func ForSignature(sig *types.Signature) *TypeParamList { return sig.TypeParams() } // RecvTypeParams returns sig.RecvTypeParams(). +// +// Deprecated: Use sig.RecvTypeParams() instead. +// +//go:fix inline func RecvTypeParams(sig *types.Signature) *TypeParamList { return sig.RecvTypeParams() } // IsComparable calls iface.IsComparable(). +// +// Deprecated: Use iface.IsComparable() instead. +// +//go:fix inline func IsComparable(iface *types.Interface) bool { return iface.IsComparable() } // IsMethodSet calls iface.IsMethodSet(). +// +// Deprecated: Use iface.IsMethodSet() instead. +// +//go:fix inline func IsMethodSet(iface *types.Interface) bool { return iface.IsMethodSet() } // IsImplicit calls iface.IsImplicit(). +// +// Deprecated: Use iface.IsImplicit() instead. +// +//go:fix inline func IsImplicit(iface *types.Interface) bool { return iface.IsImplicit() } // MarkImplicit calls iface.MarkImplicit(). +// +// Deprecated: Use iface.MarkImplicit() instead. +// +//go:fix inline func MarkImplicit(iface *types.Interface) { iface.MarkImplicit() } // ForNamed extracts the (possibly empty) type parameter object list from // named. +// +// Deprecated: Use named.TypeParams() instead. +// +//go:fix inline func ForNamed(named *types.Named) *TypeParamList { return named.TypeParams() } // SetForNamed sets the type params tparams on n. Each tparam must be of // dynamic type *types.TypeParam. +// +// Deprecated: Use n.SetTypeParams(...) instead. +// +//go:fix inline func SetForNamed(n *types.Named, tparams []*TypeParam) { n.SetTypeParams(tparams) } // NamedTypeArgs returns named.TypeArgs(). +// +// Deprecated: Use named.TypeArgs() instead. +// +//go:fix inline func NamedTypeArgs(named *types.Named) *TypeList { return named.TypeArgs() } // NamedTypeOrigin returns named.Orig(). +// +// Deprecated: Use named.Origin() instead. +// +//go:fix inline func NamedTypeOrigin(named *types.Named) types.Type { return named.Origin() } // Term is an alias for types.Term. +// +// Deprecated: Use [go/types.Term] instead. +// +//go:fix inline type Term = types.Term // NewTerm calls types.NewTerm. +// +// Deprecated: Use [go/types.NewTerm] instead. +// +//go:fix inline func NewTerm(tilde bool, typ types.Type) *Term { return types.NewTerm(tilde, typ) } // Union is an alias for types.Union +// +// Deprecated: Use [go/types.Union] instead. +// +//go:fix inline type Union = types.Union // NewUnion calls types.NewUnion. +// +// Deprecated: Use [go/types.NewUnion] instead. +// +//go:fix inline func NewUnion(terms []*Term) *Union { return types.NewUnion(terms) } // InitInstances initializes info to record information about type and function // instances. +// +// Deprecated: Use info.Instances = make(...) instead. +// +//go:fix inline func InitInstances(info *types.Info) { info.Instances = make(map[*ast.Ident]types.Instance) } // Instance is an alias for types.Instance. +// +// Deprecated: Use [go/types.Instance] instead. +// +//go:fix inline type Instance = types.Instance // GetInstances returns info.Instances. +// +// Deprecated: Use info.Instances instead. +// +//go:fix inline func GetInstances(info *types.Info) map[*ast.Ident]Instance { return info.Instances } // Context is an alias for types.Context. +// +// Deprecated: Use [go/types.Context] instead. +// +//go:fix inline type Context = types.Context // NewContext calls types.NewContext. +// +// Deprecated: Use [go/types.NewContext] instead. +// +//go:fix inline func NewContext() *Context { return types.NewContext() } // Instantiate calls types.Instantiate. +// +// Deprecated: Use [go/types.Instantiate] instead. +// +//go:fix inline func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { return types.Instantiate(ctxt, typ, targs, validate) } diff --git a/vendor/golang.org/x/exp/typeparams/typeparams_go117.go b/vendor/golang.org/x/exp/typeparams/typeparams_go117.go deleted file mode 100644 index c1da7931..00000000 --- a/vendor/golang.org/x/exp/typeparams/typeparams_go117.go +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2021 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. - -//go:build !go1.18 - -package typeparams - -import ( - "go/ast" - "go/token" - "go/types" -) - -const enabled = false - -func unsupported() { - panic("type parameters are unsupported at this go version") -} - -// IndexListExpr is a placeholder type, as type parameters are not supported at -// this Go version. Its methods panic on use. -type IndexListExpr struct { - ast.Expr - X ast.Expr // expression - Lbrack token.Pos // position of "[" - Indices []ast.Expr // index expressions - Rbrack token.Pos // position of "]" -} - -func (*IndexListExpr) Pos() token.Pos { unsupported(); return token.NoPos } -func (*IndexListExpr) End() token.Pos { unsupported(); return token.NoPos } - -// ForTypeSpec returns an empty field list, as type parameters on not supported -// at this Go version. -func ForTypeSpec(*ast.TypeSpec) *ast.FieldList { - return nil -} - -// ForFuncType returns an empty field list, as type parameters are not -// supported at this Go version. -func ForFuncType(*ast.FuncType) *ast.FieldList { - return nil -} - -// TypeParam is a placeholder type, as type parameters are not supported at -// this Go version. Its methods panic on use. -type TypeParam struct{ types.Type } - -func (*TypeParam) String() string { unsupported(); return "" } -func (*TypeParam) Underlying() types.Type { unsupported(); return nil } -func (*TypeParam) Index() int { unsupported(); return 0 } -func (*TypeParam) Constraint() types.Type { unsupported(); return nil } -func (*TypeParam) SetConstraint(types.Type) { unsupported() } -func (*TypeParam) Obj() *types.TypeName { unsupported(); return nil } - -// TypeParamList is a placeholder for an empty type parameter list. -type TypeParamList struct{} - -func (*TypeParamList) Len() int { return 0 } -func (*TypeParamList) At(int) *TypeParam { unsupported(); return nil } - -// TypeList is a placeholder for an empty type list. -type TypeList struct{} - -func (*TypeList) Len() int { return 0 } -func (*TypeList) At(int) types.Type { unsupported(); return nil } - -// NewTypeParam is unsupported at this Go version, and panics. -func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { - unsupported() - return nil -} - -// NewSignatureType calls types.NewSignature, panicking if recvTypeParams or -// typeParams is non-empty. -func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { - if len(recvTypeParams) != 0 || len(typeParams) != 0 { - unsupported() - } - return types.NewSignature(recv, params, results, variadic) -} - -// ForSignature returns an empty slice. -func ForSignature(*types.Signature) *TypeParamList { - return nil -} - -// RecvTypeParams returns a nil slice. -func RecvTypeParams(sig *types.Signature) *TypeParamList { - return nil -} - -// IsComparable returns false, as no interfaces are type-restricted at this Go -// version. -func IsComparable(*types.Interface) bool { - return false -} - -// IsMethodSet returns true, as no interfaces are type-restricted at this Go -// version. -func IsMethodSet(*types.Interface) bool { - return true -} - -// IsImplicit returns false, as no interfaces are implicit at this Go version. -func IsImplicit(*types.Interface) bool { - return false -} - -// MarkImplicit does nothing, because this Go version does not have implicit -// interfaces. -func MarkImplicit(*types.Interface) {} - -// ForNamed returns an empty type parameter list, as type parameters are not -// supported at this Go version. -func ForNamed(*types.Named) *TypeParamList { - return nil -} - -// SetForNamed panics if tparams is non-empty. -func SetForNamed(_ *types.Named, tparams []*TypeParam) { - if len(tparams) > 0 { - unsupported() - } -} - -// NamedTypeArgs returns nil. -func NamedTypeArgs(*types.Named) *TypeList { - return nil -} - -// NamedTypeOrigin is the identity method at this Go version. -func NamedTypeOrigin(named *types.Named) types.Type { - return named -} - -// Term holds information about a structural type restriction. -type Term struct { - tilde bool - typ types.Type -} - -func (m *Term) Tilde() bool { return m.tilde } -func (m *Term) Type() types.Type { return m.typ } -func (m *Term) String() string { - pre := "" - if m.tilde { - pre = "~" - } - return pre + m.typ.String() -} - -// NewTerm creates a new placeholder term type. -func NewTerm(tilde bool, typ types.Type) *Term { - return &Term{tilde, typ} -} - -// Union is a placeholder type, as type parameters are not supported at this Go -// version. Its methods panic on use. -type Union struct{ types.Type } - -func (*Union) String() string { unsupported(); return "" } -func (*Union) Underlying() types.Type { unsupported(); return nil } -func (*Union) Len() int { return 0 } -func (*Union) Term(i int) *Term { unsupported(); return nil } - -// NewUnion is unsupported at this Go version, and panics. -func NewUnion(terms []*Term) *Union { - unsupported() - return nil -} - -// InitInstances is a noop at this Go version. -func InitInstances(*types.Info) {} - -// Instance is a placeholder type, as type parameters are not supported at this -// Go version. -type Instance struct { - TypeArgs *TypeList - Type types.Type -} - -// GetInstances returns a nil map, as type parameters are not supported at this -// Go version. -func GetInstances(info *types.Info) map[*ast.Ident]Instance { return nil } - -// Context is a placeholder type, as type parameters are not supported at -// this Go version. -type Context struct{} - -// NewContext returns a placeholder Context instance. -func NewContext() *Context { - return &Context{} -} - -// Instantiate is unsupported on this Go version, and panics. -func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { - unsupported() - return nil, nil -} diff --git a/vendor/golang.org/x/exp/typeparams/typeterm.go b/vendor/golang.org/x/exp/typeparams/typeterm.go index 7350bb70..6b825729 100644 --- a/vendor/golang.org/x/exp/typeparams/typeterm.go +++ b/vendor/golang.org/x/exp/typeparams/typeterm.go @@ -1,9 +1,10 @@ +// Code generated by copytermlist.go DO NOT EDIT. +// Source: GOROOT/src/go/types/typeterm.go + // Copyright 2021 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 copytermlist.go DO NOT EDIT. - package typeparams import "go/types" @@ -119,7 +120,7 @@ func (x *term) includes(t types.Type) bool { u := t if x.tilde { - u = under(u) + u = u.Underlying() } return types.Identical(x.typ, u) } @@ -159,11 +160,11 @@ func (x *term) disjoint(y *term) bool { } ux := x.typ if y.tilde { - ux = under(ux) + ux = ux.Underlying() } uy := y.typ if x.tilde { - uy = under(uy) + uy = uy.Underlying() } return !types.Identical(ux, uy) } diff --git a/vendor/golang.org/x/mod/modfile/read.go b/vendor/golang.org/x/mod/modfile/read.go index 5b528c71..9e35e1ac 100644 --- a/vendor/golang.org/x/mod/modfile/read.go +++ b/vendor/golang.org/x/mod/modfile/read.go @@ -924,7 +924,7 @@ var ( moduleStr = []byte("module") ) -// ModulePath returns the module path from the gomod file text. +// ModulePath returns the module path from the go.mod file text. // If it cannot find a module path, it returns an empty string. // It is tolerant of unrelated problems in the go.mod file. func ModulePath(mod []byte) string { diff --git a/vendor/golang.org/x/mod/modfile/rule.go b/vendor/golang.org/x/mod/modfile/rule.go index 9ab203b5..20ba825d 100644 --- a/vendor/golang.org/x/mod/modfile/rule.go +++ b/vendor/golang.org/x/mod/modfile/rule.go @@ -1477,7 +1477,7 @@ func (f *File) setRequireSeparateIndirect(req []*Require, simplify bool) { // Delete requirements we don't want anymore. // Update versions and indirect comments on requirements we want to keep. // If a requirement is in last{Direct,Indirect}Block with the wrong - // indirect marking after this, or if the requirement is in an single + // indirect marking after this, or if the requirement is in a single // uncommented mixed block (oneFlatUncommentedBlock), move it to the // correct block. // @@ -1537,7 +1537,7 @@ func (f *File) DropRequire(path string) error { return nil } -// AddExclude adds a exclude statement to the mod file. Errors if the provided +// AddExclude adds an exclude statement to the mod file. Errors if the provided // version is not a canonical version string func (f *File) AddExclude(path, vers string) error { if err := checkCanonicalVersion(path, vers); err != nil { @@ -1708,7 +1708,7 @@ func (f *File) AddIgnore(path string) error { return nil } -// DropIgnore removes a ignore directive with the given path. +// DropIgnore removes an ignore directive with the given path. // It does nothing if no such ignore directive exists. func (f *File) DropIgnore(path string) error { for _, t := range f.Ignore { diff --git a/vendor/golang.org/x/tools/go/analysis/passes/fieldalignment/fieldalignment.go b/vendor/golang.org/x/tools/go/analysis/passes/fieldalignment/fieldalignment.go index 53c74634..02791bc7 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/fieldalignment/fieldalignment.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/fieldalignment/fieldalignment.go @@ -14,6 +14,7 @@ import ( "go/token" "go/types" "sort" + "strings" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" @@ -68,37 +69,66 @@ var Analyzer = &analysis.Analyzer{ func run(pass *analysis.Pass) (any, error) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - nodeFilter := []ast.Node{ - (*ast.StructType)(nil), - } - inspect.Preorder(nodeFilter, func(node ast.Node) { - var s *ast.StructType - var ok bool - if s, ok = node.(*ast.StructType); !ok { - return - } - if tv, ok := pass.TypesInfo.Types[s]; ok { - fieldalignment(pass, s, tv.Type.(*types.Struct)) + for curStruct := range inspect.Root().Preorder((*ast.StructType)(nil)) { + s := curStruct.Node().(*ast.StructType) + // For every named struct defined as "type Name struct { ... }", + // the *ast.StructType node has a parent *ast.TypeSpec, + // which contains the struct's name in its Name field. + name := "struct" // (anonymous) + if spec, ok := curStruct.Parent().Node().(*ast.TypeSpec); ok { + name = spec.Name.Name } - }) + fieldalignment(pass, s, name) + } + return nil, nil } -var unsafePointerTyp = types.Unsafe.Scope().Lookup("Pointer").(*types.TypeName).Type() +func fieldalignment(pass *analysis.Pass, node *ast.StructType, name string) { + var ( + sizes = &gcSizes{ + wordSize: pass.TypesSizes.Sizeof(types.Typ[types.UnsafePointer]), + maxAlign: pass.TypesSizes.Alignof(types.Typ[types.UnsafePointer]), + } -func fieldalignment(pass *analysis.Pass, node *ast.StructType, typ *types.Struct) { - wordSize := pass.TypesSizes.Sizeof(unsafePointerTyp) - maxAlign := pass.TypesSizes.Alignof(unsafePointerTyp) + typ = pass.TypesInfo.TypeOf(node).(*types.Struct) + optimal, indexes = optimalOrder(typ, sizes) + + actualSize = sizes.sizeof(typ) + actualPtrs = sizes.ptrdata(typ) + + optimalSize = sizes.sizeof(optimal) + optimalPtrs = sizes.ptrdata(optimal) + ) + + var message strings.Builder + if actualSize != optimalSize { + // Struct could be smaller. + // TODO(adonovan): IMHO the criterion should be "significantly smaller". + fmt.Fprintf(&message, "%s has size %d", name, actualSize) + actualClass := classSize(actualSize) + if actualClass == -1 { + actualClass = actualSize + fmt.Fprint(&message, " (uses global allocator)") + } else if actualClass != actualSize { + fmt.Fprintf(&message, " (allocator size class %d)", actualClass) + } - s := gcSizes{wordSize, maxAlign} - optimal, indexes := optimalOrder(typ, &s) - optsz, optptrs := s.Sizeof(optimal), s.ptrdata(optimal) + fmt.Fprintf(&message, " but the optimal size is %d", optimalSize) + optimalClass := classSize(optimalSize) + if optimalClass == -1 { + optimalClass = optimalSize + } else if optimalClass != optimalSize { + fmt.Fprintf(&message, " (allocator size class %d)", optimalClass) + } - var message string - if sz := s.Sizeof(typ); sz != optsz { - message = fmt.Sprintf("struct of size %d could be %d", sz, optsz) - } else if ptrs := s.ptrdata(typ); ptrs != optptrs { - message = fmt.Sprintf("struct with %d pointer bytes could be %d", ptrs, optptrs) + wastage := actualClass - optimalClass + if percentage := wastage * 100 / actualClass; percentage > 25 { + fmt.Fprintf(&message, " leading to a waste of %d bytes (%d%%)", wastage, percentage) + } + } else if actualPtrs != optimalPtrs { + // Struct could place pointers more efficiently for GC marking. + fmt.Fprintf(&message, "%s has %d leading bytes of pointer data but optimal value is %d", name, actualPtrs, optimalPtrs) } else { // Already optimal order. return @@ -151,7 +181,7 @@ func fieldalignment(pass *analysis.Pass, node *ast.StructType, typ *types.Struct pass.Report(analysis.Diagnostic{ Pos: node.Pos(), End: node.Pos() + token.Pos(len("struct")), - Message: message, + Message: message.String(), SuggestedFixes: []analysis.SuggestedFix{{ Message: "Rearrange fields", TextEdits: []analysis.TextEdit{{ @@ -179,8 +209,8 @@ func optimalOrder(str *types.Struct, sizes *gcSizes) (*types.Struct, []int) { ft := field.Type() elems[i] = elem{ i, - sizes.Alignof(ft), - sizes.Sizeof(ft), + sizes.alignof(ft), + sizes.sizeof(ft), sizes.ptrdata(ft), } } @@ -240,40 +270,42 @@ func optimalOrder(str *types.Struct, sizes *gcSizes) (*types.Struct, []int) { return types.NewStruct(fields, nil), indexes } -// Code below based on go/types.StdSizes. +// gcSizes implements cmd/compile layout rules, providing ptrdata (GC +// scanning limits) and trailing zero-size field padding not available +// in [types.Sizes]. type gcSizes struct { - WordSize int64 - MaxAlign int64 + wordSize int64 + maxAlign int64 } -func (s *gcSizes) Alignof(T types.Type) int64 { +func (s *gcSizes) alignof(T types.Type) int64 { // For arrays and structs, alignment is defined in terms // of alignment of the elements and fields, respectively. switch t := T.Underlying().(type) { case *types.Array: // spec: "For a variable x of array type: unsafe.Alignof(x) // is the same as unsafe.Alignof(x[0]), but at least 1." - return s.Alignof(t.Elem()) + return s.alignof(t.Elem()) case *types.Struct: // spec: "For a variable x of struct type: unsafe.Alignof(x) // is the largest of the values unsafe.Alignof(x.f) for each // field f of x, but at least 1." max := int64(1) for i, nf := 0, t.NumFields(); i < nf; i++ { - if a := s.Alignof(t.Field(i).Type()); a > max { + if a := s.alignof(t.Field(i).Type()); a > max { max = a } } return max } - a := s.Sizeof(T) // may be 0 + a := s.sizeof(T) // may be 0 // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." if a < 1 { return 1 } - if a > s.MaxAlign { - return s.MaxAlign + if a > s.maxAlign { + return s.maxAlign } return a } @@ -294,7 +326,7 @@ var basicSizes = [...]byte{ types.Complex128: 16, } -func (s *gcSizes) Sizeof(T types.Type) int64 { +func (s *gcSizes) sizeof(T types.Type) int64 { switch t := T.Underlying().(type) { case *types.Basic: k := t.Kind() @@ -304,12 +336,12 @@ func (s *gcSizes) Sizeof(T types.Type) int64 { } } if k == types.String { - return s.WordSize * 2 + return s.wordSize * 2 } case *types.Array: - return t.Len() * s.Sizeof(t.Elem()) + return t.Len() * s.sizeof(t.Elem()) case *types.Slice: - return s.WordSize * 3 + return s.wordSize * 3 case *types.Struct: nf := t.NumFields() if nf == 0 { @@ -320,7 +352,7 @@ func (s *gcSizes) Sizeof(T types.Type) int64 { max := int64(1) for i := range nf { ft := t.Field(i).Type() - a, sz := s.Alignof(ft), s.Sizeof(ft) + a, sz := s.alignof(ft), s.sizeof(ft) if a > max { max = a } @@ -331,9 +363,9 @@ func (s *gcSizes) Sizeof(T types.Type) int64 { } return align(o, max) case *types.Interface: - return s.WordSize * 2 + return s.wordSize * 2 } - return s.WordSize // catch-all + return s.wordSize // catch-all } // align returns the smallest y >= x such that y % a == 0. @@ -347,13 +379,13 @@ func (s *gcSizes) ptrdata(T types.Type) int64 { case *types.Basic: switch t.Kind() { case types.String, types.UnsafePointer: - return s.WordSize + return s.wordSize } return 0 case *types.Chan, *types.Map, *types.Pointer, *types.Signature, *types.Slice: - return s.WordSize + return s.wordSize case *types.Interface: - return 2 * s.WordSize + return 2 * s.wordSize case *types.Array: n := t.Len() if n == 0 { @@ -363,7 +395,7 @@ func (s *gcSizes) ptrdata(T types.Type) int64 { if a == 0 { return 0 } - z := s.Sizeof(t.Elem()) + z := s.sizeof(t.Elem()) return (n-1)*z + a case *types.Struct: nf := t.NumFields() @@ -374,7 +406,7 @@ func (s *gcSizes) ptrdata(T types.Type) int64 { var o, p int64 for i := range nf { ft := t.Field(i).Type() - a, sz := s.Alignof(ft), s.Sizeof(ft) + a, sz := s.alignof(ft), s.sizeof(ft) fp := s.ptrdata(ft) o = align(o, a) if fp != 0 { @@ -387,3 +419,16 @@ func (s *gcSizes) ptrdata(T types.Type) int64 { panic("impossible") } + +// Code below based on tools/gopls/internal/golang/hover.go + +// classSize reports the size class for a struct of the specified size, or -1 if unknown. +// See GOROOT/src/runtime/msize.go for details. +func classSize(size int64) int64 { + if size > 1<<15 { + return -1 // avoid allocation + } + // We assume that bytes.Clone doesn't trim, + // and reports the underlying size class + return int64(cap(bytes.Clone(make([]byte, size)))) +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/inline/doc.go b/vendor/golang.org/x/tools/go/analysis/passes/inline/doc.go new file mode 100644 index 00000000..fea596b9 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/inline/doc.go @@ -0,0 +1,130 @@ +// Copyright 2025 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 inline defines an analyzer that inlines calls to functions +and uses of constants marked with a "//go:fix inline" directive. + +# Analyzer inline + +inline: apply fixes based on 'go:fix inline' comment directives + +The inline analyzer inlines functions, constants, and type aliases +that are marked for inlining. + +Use this command to apply (just) inline fixes en masse: + + $ go fix -inline ./... + +## Functions + +Given a function that is marked for inlining, like this one: + + //go:fix inline + func Square(x int) int { return Pow(x, 2) } + +this analyzer will recommend that calls to the function elsewhere, in the same +or other packages, should be inlined. + +Inlining can be used to move off of a deprecated function: + + // Deprecated: prefer Pow(x, 2). + //go:fix inline + func Square(x int) int { return Pow(x, 2) } + +It can also be used to move off of an obsolete package, +as when the import path has changed or a higher major version is available: + + package pkg + + import pkg2 "pkg/v2" + + //go:fix inline + func F() { pkg2.F(nil) } + +Replacing a call pkg.F() by pkg2.F(nil) can have no effect on the program, +so this mechanism provides a low-risk way to update large numbers of calls. +We recommend, where possible, expressing the old API in terms of the new one +to enable automatic migration. + +The inliner takes care to avoid behavior changes, even subtle ones, +such as changes to the order in which argument expressions are +evaluated. When it cannot safely eliminate all parameter variables, +it may introduce a "binding declaration" of the form + + var params = args + +to evaluate argument expressions in the correct order and bind them to +parameter variables. Since the resulting code transformation may be +stylistically suboptimal, such inlinings may be disabled by specifying +the -inline.allow_binding_decl=false flag to the analyzer driver. + +(In cases where it is not safe to "reduce" a call—that is, to replace +a call f(x) by the body of function f, suitably substituted—the +inliner machinery is capable of replacing f by a function literal, +func(){...}(). However, the inline analyzer discards all such +"literalizations" unconditionally, again on grounds of style.) + +## Constants + +Given a constant that is marked for inlining, like this one: + + //go:fix inline + const Ptr = Pointer + +this analyzer will recommend that uses of Ptr should be replaced with Pointer. + +As with functions, inlining can be used to replace deprecated constants and +constants in obsolete packages. + +A constant definition can be marked for inlining only if it refers to another +named constant. + +The "//go:fix inline" comment must appear before a single const declaration on its own, +as above; before a const declaration that is part of a group, as in this case: + + const ( + C = 1 + //go:fix inline + Ptr = Pointer + ) + +or before a group, applying to every constant in the group: + + //go:fix inline + const ( + Ptr = Pointer + Val = Value + ) + +## Type aliases + +Similar to named constants, a type alias can also be marked for inlining: + + //go:fix inline + type A = newpkg.A + +The analyzer will replace all references to the annotated type +(A) by the type on the right-hand side of the declaration (newpkg.A). + +## Tests + +A use of a function, named constant, or type alias X from its +dedicated test (TestX), is not inlined, since the purpose of the test +is to exercise X itself, even if it is deprecated and other uses of it +should be inlined. +This applies to benchmarks and examples too, and follows the usual +conventions of test function naming. + +Similarly, if the symbol X is declared in a file named foo.go, any use +of it within a file named foo_test.go will also not be inlined. + +# Analyzer gofixdirective + +gofixdirective: validate uses of //go:fix comment directives + +The gofixdirective analyzer checks "//go:fix inline" directives for correctness. +See the documentation for the gofix analyzer for more about "/go:fix inline". +*/ +package inline diff --git a/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go b/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go new file mode 100644 index 00000000..d16e0d0b --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go @@ -0,0 +1,638 @@ +// Copyright 2023 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 inline + +import ( + "fmt" + "go/ast" + "go/types" + "slices" + "strings" + + _ "embed" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/analysis/passes/internal/gofixdirective" + "golang.org/x/tools/go/ast/edge" + "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysis/analyzerutil" + typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" + "golang.org/x/tools/internal/astutil" + "golang.org/x/tools/internal/moreiters" + "golang.org/x/tools/internal/packagepath" + "golang.org/x/tools/internal/refactor" + "golang.org/x/tools/internal/refactor/inline" + "golang.org/x/tools/internal/typesinternal" + "golang.org/x/tools/internal/typesinternal/typeindex" +) + +//go:embed doc.go +var doc string + +var Analyzer = &analysis.Analyzer{ + Name: "inline", + Doc: analyzerutil.MustExtractDoc(doc, "inline"), + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/inline", + Run: run, + FactTypes: []analysis.Fact{ + (*goFixInlineFuncFact)(nil), + (*goFixInlineConstFact)(nil), + (*goFixInlineAliasFact)(nil), + }, + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + typeindexanalyzer.Analyzer, + }, +} + +var ( + allowBindingDecl bool + lazyEdits bool +) + +func init() { + Analyzer.Flags.BoolVar(&allowBindingDecl, "allow_binding_decl", false, + "permit inlinings that require a 'var params = args' declaration") + Analyzer.Flags.BoolVar(&lazyEdits, "lazy_edits", false, + "compute edits lazily (only meaningful to gopls driver)") +} + +// analyzer holds the state for this analysis. +type analyzer struct { + pass *analysis.Pass + root inspector.Cursor + index *typeindex.Index + // memoization of repeated calls for same file. + fileContent map[string][]byte + // memoization of fact imports (nil => no fact) + inlinableFuncs map[*types.Func]*inline.Callee + inlinableConsts map[*types.Const]*goFixInlineConstFact + inlinableAliases map[*types.TypeName]*goFixInlineAliasFact +} + +func run(pass *analysis.Pass) (any, error) { + a := &analyzer{ + pass: pass, + root: pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Root(), + index: pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index), + fileContent: make(map[string][]byte), + inlinableFuncs: make(map[*types.Func]*inline.Callee), + inlinableConsts: make(map[*types.Const]*goFixInlineConstFact), + inlinableAliases: make(map[*types.TypeName]*goFixInlineAliasFact), + } + gofixdirective.Find(pass, a.root, a) + a.inline() + return nil, nil +} + +// HandleFunc exports a fact for functions marked with go:fix. +func (a *analyzer) HandleFunc(decl *ast.FuncDecl) { + content, err := a.readFile(decl) + if err != nil { + a.pass.Reportf(decl.Doc.Pos(), "invalid inlining candidate: cannot read source file: %v", err) + return + } + callee, err := inline.AnalyzeCallee(discard, a.pass.Fset, a.pass.Pkg, a.pass.TypesInfo, decl, content) + if err != nil { + a.pass.Reportf(decl.Doc.Pos(), "invalid inlining candidate: %v", err) + return + } + fn := a.pass.TypesInfo.Defs[decl.Name].(*types.Func) + a.pass.ExportObjectFact(fn, &goFixInlineFuncFact{callee}) + a.inlinableFuncs[fn] = callee +} + +// HandleAlias exports a fact for aliases marked with go:fix. +func (a *analyzer) HandleAlias(spec *ast.TypeSpec) { + // Remember that this is an inlinable alias. + typ := &goFixInlineAliasFact{} + lhs := a.pass.TypesInfo.Defs[spec.Name].(*types.TypeName) + a.inlinableAliases[lhs] = typ + // Create a fact only if the LHS is exported and defined at top level. + // We create a fact even if the RHS is non-exported, + // so we can warn about uses in other packages. + if lhs.Exported() && typesinternal.IsPackageLevel(lhs) { + a.pass.ExportObjectFact(lhs, typ) + } +} + +// HandleConst exports a fact for constants marked with go:fix. +func (a *analyzer) HandleConst(nameIdent, rhsIdent *ast.Ident) { + lhs := a.pass.TypesInfo.Defs[nameIdent].(*types.Const) + rhs := a.pass.TypesInfo.Uses[rhsIdent].(*types.Const) // must be so in a well-typed program + con := &goFixInlineConstFact{ + RHSName: rhs.Name(), + RHSPkgName: rhs.Pkg().Name(), + RHSPkgPath: rhs.Pkg().Path(), + } + if rhs.Pkg() == a.pass.Pkg { + con.rhsObj = rhs + } + a.inlinableConsts[lhs] = con + // Create a fact only if the LHS is exported and defined at top level. + // We create a fact even if the RHS is non-exported, + // so we can warn about uses in other packages. + if lhs.Exported() && typesinternal.IsPackageLevel(lhs) { + a.pass.ExportObjectFact(lhs, con) + } +} + +// inline inlines each static call to an inlinable function +// and each reference to an inlinable constant or type alias. +func (a *analyzer) inline() { + for cur := range a.root.Preorder((*ast.CallExpr)(nil), (*ast.Ident)(nil)) { + switch n := cur.Node().(type) { + case *ast.CallExpr: + a.inlineCall(n, cur) + + case *ast.Ident: + switch obj := a.pass.TypesInfo.Uses[n].(type) { + case *types.TypeName: + a.inlineAlias(obj, cur) + case *types.Const: + a.inlineConst(obj, cur) + } + } + } +} + +// If call is a call to an inlinable func, suggest inlining its use at cur. +func (a *analyzer) inlineCall(call *ast.CallExpr, cur inspector.Cursor) { + if fn := typeutil.StaticCallee(a.pass.TypesInfo, call); fn != nil { + // Inlinable? + callee, ok := a.inlinableFuncs[fn] + if !ok { + var fact goFixInlineFuncFact + if a.pass.ImportObjectFact(fn, &fact) { + callee = fact.Callee + a.inlinableFuncs[fn] = callee + } + } + if callee == nil { + return // nope + } + + if a.withinTestOf(cur, fn) { + return // don't inline a function from within its own test + } + + // Compute the edits. + // + // Ordinarily the analyzer reports a fix containing + // edits. However, the algorithm is somewhat expensive + // (unnecessarily so: see go.dev/issue/75773) so + // to reduce costs in gopls, we omit the edits, + // meaning that gopls must compute them on demand + // (based on the Diagnostic.Category) when they are + // requested via a code action. + // + // This does mean that the following categories of + // caller-dependent obstacles to inlining will be + // reported when the gopls user requests the fix, + // rather than by quietly suppressing the diagnostic: + // - shadowing problems + // - callee imports inaccessible "internal" packages + // - callee refers to nonexported symbols + // - callee uses too-new Go features + // - inlining call from a cgo file + var edits []analysis.TextEdit + if !lazyEdits { + // Inline the call. + caller := &inline.Caller{ + Fset: a.pass.Fset, + Types: a.pass.Pkg, + Info: a.pass.TypesInfo, + File: astutil.EnclosingFile(cur), + Call: call, + CountUses: func(pkgname *types.PkgName) int { + return moreiters.Len(a.index.Uses(pkgname)) + }, + } + res, err := inline.Inline(caller, callee, &inline.Options{Logf: discard}) + if err != nil { + a.pass.Reportf(call.Lparen, "%v", err) + return + } + + if res.Literalized { + // Users are not fond of inlinings that literalize + // f(x) to func() { ... }(), so avoid them. + // + // (Unfortunately the inliner is very timid, + // and often literalizes when it cannot prove that + // reducing the call is safe; the user of this tool + // has no indication of what the problem is.) + return + } + if res.BindingDecl && !allowBindingDecl { + // When applying fix en masse, users are similarly + // unenthusiastic about inlinings that cannot + // entirely eliminate the parameters and + // insert a 'var params = args' declaration. + // The flag allows them to decline such fixes. + return + } + edits = res.Edits + } + + a.pass.Report(analysis.Diagnostic{ + Pos: call.Pos(), + End: call.End(), + Message: fmt.Sprintf("Call of %v should be inlined", callee), + Category: "inline_call", // keep consistent with gopls/internal/golang.fixInlineCall + SuggestedFixes: []analysis.SuggestedFix{{ + Message: fmt.Sprintf("Inline call of %v", callee), + TextEdits: edits, // within gopls, this is nil => compute fix's edits lazily + }}, + }) + } +} + +// withinTestOf reports whether curUse is within a dedicated test +// function for the inlinable target symbol. +// A call within its dedicated test should not be inlined. +func (a *analyzer) withinTestOf(curUse inspector.Cursor, target types.Object) bool { + // x_test.go -> x + useFileBase, isTest := strings.CutSuffix(a.pass.Fset.File(curUse.Node().Pos()).Name(), "_test.go") + if !isTest { + return false // not a test file + } + + // Suppress fixes for uses in x_test.go of target symbol defined in x.go (#79272). + if useFileBase == strings.TrimSuffix(a.pass.Fset.File(target.Pos()).Name(), ".go") { + return true + } + + curFuncDecl, ok := moreiters.First(curUse.Enclosing((*ast.FuncDecl)(nil))) + if !ok { + return false // not in a function + } + funcDecl := curFuncDecl.Node().(*ast.FuncDecl) + if funcDecl.Recv != nil { + return false // not a test func + } + if strings.TrimSuffix(a.pass.Pkg.Path(), "_test") != target.Pkg().Path() { + return false // different package + } + + // Computed expected SYMBOL portion of "ExampleSYMBOL_comment" + // for the target symbol. (Strictly, this convention applies + // only to Example functions.) + // TODO(adonovan): use a proper Test function parser. + symbol := target.Name() + if fn, ok := target.(*types.Func); ok { + if recv := fn.Signature().Recv(); recv != nil { + _, named := typesinternal.ReceiverNamed(recv) + symbol = named.Obj().Name() + "_" + symbol + } + } + fname := funcDecl.Name.Name + for _, pre := range []string{"Test", "Example", "Bench", "Fuzz"} { + if fname == pre+symbol || strings.HasPrefix(fname, pre+symbol+"_") { + return true // use of X within TestX + } + } + + return false +} + +// If tn is the TypeName of an inlinable alias, suggest inlining its use at cur. +func (a *analyzer) inlineAlias(tn *types.TypeName, curId inspector.Cursor) { + inalias, ok := a.inlinableAliases[tn] + if !ok { + var fact goFixInlineAliasFact + if a.pass.ImportObjectFact(tn, &fact) { + inalias = &fact + a.inlinableAliases[tn] = inalias + } + } + if inalias == nil { + return // nope + } + + if a.withinTestOf(curId, tn) { + return // don't inline a type alias from within its own test + } + + alias := tn.Type().(*types.Alias) + // Remember the names of the alias's type params. When we check for shadowing + // later, we'll ignore these because they won't appear in the replacement text. + typeParamNames := map[*types.TypeName]bool{} + for tp := range alias.TypeParams().TypeParams() { + typeParamNames[tp.Obj()] = true + } + rhs := alias.Rhs() + curPath := a.pass.Pkg.Path() + curFile := astutil.EnclosingFile(curId) + id := curId.Node().(*ast.Ident) + + // Find the complete identifier, which may take any of these forms: + // Id + // Id[T] + // Id[K, V] + // pkg.Id + // pkg.Id[T] + // pkg.Id[K, V] + var expr ast.Expr = id + if curId.ParentEdgeKind() == edge.SelectorExpr_Sel { + curId = curId.Parent() + expr = curId.Node().(ast.Expr) + } + // If expr is part of an IndexExpr or IndexListExpr, we'll need that node. + // Given C[int], TypeOf(C) is generic but TypeOf(C[int]) is instantiated. + switch curId.ParentEdgeKind() { + case edge.IndexExpr_X: + curId = curId.Parent() + expr = curId.Node().(*ast.IndexExpr) + case edge.IndexListExpr_X: + curId = curId.Parent() + expr = curId.Node().(*ast.IndexListExpr) + } + + // Reject inlining of a type alias used to declare an embedded + // struct field if doing so would change the field's name. + if v, ok := a.pass.TypesInfo.Defs[id].(*types.Var); ok && v.Embedded() { + identicalName := false + // TODO(adonovan): should we allow a pointer (type A = *pkg.A)? + if rhs, ok := alias.Rhs().(*types.Named); ok { + identicalName = alias.Obj().Name() == rhs.Obj().Name() + } + if !identicalName { + // Type is embedded, inlining the alias will cause + // the field name to be changed, which might break + // programs in terms of backwards compatibility. + return + } + } + + t := a.pass.TypesInfo.TypeOf(expr).(*types.Alias) // type of entire identifier + if targs := t.TypeArgs(); targs.Len() > 0 { + // Instantiate the alias with the type args from this use. + // For example, given type A = M[K, V], compute the type of the use + // A[int, Foo] as M[int, Foo]. + // Don't validate instantiation: it can't panic unless we have a bug, + // in which case seeing the stack trace via telemetry would be helpful. + instAlias, _ := types.Instantiate(nil, alias, slices.Collect(targs.Types()), false) + rhs = instAlias.(*types.Alias).Rhs() + } + + // We have an identifier A here (n), possibly qualified by a package + // identifier (sel.n), and an inlinable "type A = rhs" elsewhere. + // + // We can replace A with rhs if no name in rhs is shadowed at n's position, + // and every package in rhs is importable by the current package. + var ( + importPrefixes = map[string]string{curPath: ""} // from pkg path to prefix + edits []analysis.TextEdit + ) + for _, tn := range typenames(rhs) { + // Ignore the type parameters of the alias: they won't appear in the result. + if typeParamNames[tn] { + continue + } + var pkgPath, pkgName string + if pkg := tn.Pkg(); pkg != nil { + pkgPath = pkg.Path() + pkgName = pkg.Name() + } + if pkgPath == "" || pkgPath == curPath { + // The name is in the current package or the universe scope, so no import + // is required. Check that it is not shadowed (that is, that the type + // it refers to in rhs is the same one it refers to at n). + scope := a.pass.TypesInfo.Scopes[curFile].Innermost(id.Pos()) // n's scope + _, obj := scope.LookupParent(tn.Name(), id.Pos()) // what qn.name means in n's scope + if obj != tn { + return + } + } else if !packagepath.CanImport(a.pass.Pkg.Path(), pkgPath) { + // If this package can't see the package of this part of rhs, we can't inline. + return + } else if _, ok := importPrefixes[pkgPath]; !ok { + // Use AddImport to add pkgPath if it's not there already. Associate the prefix it assigns + // with the prefix it assigns + // with the package path for use by the TypeString qualifier below. + prefix, eds := refactor.AddImport( + a.pass.TypesInfo, curFile, pkgName, pkgPath, tn.Name(), id.Pos()) + importPrefixes[pkgPath] = strings.TrimSuffix(prefix, ".") + edits = append(edits, eds...) + } + } + + // To get the replacement text, render the alias RHS using the package prefixes + // we assigned above. + newText := types.TypeString(rhs, func(p *types.Package) string { + if p == a.pass.Pkg { + return "" + } + if prefix, ok := importPrefixes[p.Path()]; ok { + return prefix + } + panic(fmt.Sprintf("in %q, package path %q has no import prefix", rhs, p.Path())) + }) + a.reportInline("type alias", "Type alias", expr, edits, newText) +} + +// typenames returns the TypeNames for types within t (including t itself) that have +// them: basic types, named types and alias types. +// The same name may appear more than once. +func typenames(t types.Type) []*types.TypeName { + var tns []*types.TypeName + + var visit func(types.Type) + visit = func(t types.Type) { + if hasName, ok := t.(interface{ Obj() *types.TypeName }); ok { + tns = append(tns, hasName.Obj()) + } + switch t := t.(type) { + case *types.Basic: + tns = append(tns, types.Universe.Lookup(t.Name()).(*types.TypeName)) + case *types.Named: + for t := range t.TypeArgs().Types() { + visit(t) + } + case *types.Alias: + for t := range t.TypeArgs().Types() { + visit(t) + } + case *types.TypeParam: + tns = append(tns, t.Obj()) + case *types.Pointer: + visit(t.Elem()) + case *types.Slice: + visit(t.Elem()) + case *types.Array: + visit(t.Elem()) + case *types.Chan: + visit(t.Elem()) + case *types.Map: + visit(t.Key()) + visit(t.Elem()) + case *types.Struct: + for field := range t.Fields() { + visit(field.Type()) + } + case *types.Signature: + // Ignore the receiver: although it may be present, it has no meaning + // in a type expression. + // Ditto for receiver type params. + // Also, function type params cannot appear in a type expression. + if t.TypeParams() != nil { + panic("Signature.TypeParams in type expression") + } + visit(t.Params()) + visit(t.Results()) + case *types.Interface: + for etyp := range t.EmbeddedTypes() { + visit(etyp) + } + for method := range t.ExplicitMethods() { + visit(method.Type()) + } + case *types.Tuple: + for v := range t.Variables() { + visit(v.Type()) + } + case *types.Union: + panic("Union in type expression") + default: + panic(fmt.Sprintf("unknown type %T", t)) + } + } + + visit(t) + + return tns +} + +// If con is an inlinable constant, suggest inlining its use at cur. +func (a *analyzer) inlineConst(con *types.Const, cur inspector.Cursor) { + incon, ok := a.inlinableConsts[con] + if !ok { + var fact goFixInlineConstFact + if a.pass.ImportObjectFact(con, &fact) { + incon = &fact + a.inlinableConsts[con] = incon + } + } + if incon == nil { + return // nope + } + + if a.withinTestOf(cur, con) { + return // don't inline a type alias from within its own test + } + + // If n is qualified by a package identifier, we'll need the full selector expression. + curFile := astutil.EnclosingFile(cur) + n := cur.Node().(*ast.Ident) + + // We have an identifier A here (n), possibly qualified by a package identifier (sel.X, + // where sel is the parent of n), // and an inlinable "const A = B" elsewhere (incon). + // Consider replacing A with B. + + // Check that the expression we are inlining (B) means the same thing + // (refers to the same object) in n's scope as it does in A's scope. + // If the RHS is not in the current package, AddImport will handle + // shadowing, so we only need to worry about when both expressions + // are in the current package. + if a.pass.Pkg.Path() == incon.RHSPkgPath { + // incon.rhsObj is the object referred to by B in the definition of A. + scope := a.pass.TypesInfo.Scopes[curFile].Innermost(n.Pos()) // n's scope + _, obj := scope.LookupParent(incon.RHSName, n.Pos()) // what "B" means in n's scope + if obj == nil { + // Should be impossible: if code at n can refer to the LHS, + // it can refer to the RHS. + panic(fmt.Sprintf("no object for inlinable const %s RHS %s", n.Name, incon.RHSName)) + } + if obj != incon.rhsObj { + // "B" means something different here than at the inlinable const's scope. + return + } + } else if !packagepath.CanImport(a.pass.Pkg.Path(), incon.RHSPkgPath) { + // If this package can't see the RHS's package, we can't inline. + return + } + var ( + importPrefix string + edits []analysis.TextEdit + ) + if incon.RHSPkgPath != a.pass.Pkg.Path() { + importPrefix, edits = refactor.AddImport( + a.pass.TypesInfo, curFile, incon.RHSPkgName, incon.RHSPkgPath, incon.RHSName, n.Pos()) + } + // If n is qualified by a package identifier, we'll need the full selector expression. + var expr ast.Expr = n + if cur.ParentEdgeKind() == edge.SelectorExpr_Sel { + expr = cur.Parent().Node().(ast.Expr) + } + a.reportInline("constant", "Constant", expr, edits, importPrefix+incon.RHSName) +} + +// reportInline reports a diagnostic for fixing an inlinable name. +func (a *analyzer) reportInline(kind, capKind string, ident ast.Expr, edits []analysis.TextEdit, newText string) { + edits = append(edits, analysis.TextEdit{ + Pos: ident.Pos(), + End: ident.End(), + NewText: []byte(newText), + }) + name := astutil.Format(a.pass.Fset, ident) + a.pass.Report(analysis.Diagnostic{ + Pos: ident.Pos(), + End: ident.End(), + Message: fmt.Sprintf("%s %s should be inlined", capKind, name), + SuggestedFixes: []analysis.SuggestedFix{{ + Message: fmt.Sprintf("Inline %s %s", kind, name), + TextEdits: edits, + }}, + }) +} + +func (a *analyzer) readFile(node ast.Node) ([]byte, error) { + filename := a.pass.Fset.File(node.Pos()).Name() + content, ok := a.fileContent[filename] + if !ok { + var err error + content, err = a.pass.ReadFile(filename) + if err != nil { + return nil, err + } + a.fileContent[filename] = content + } + return content, nil +} + +// A goFixInlineFuncFact is exported for each function marked "//go:fix inline". +// It holds information about the callee to support inlining. +type goFixInlineFuncFact struct{ Callee *inline.Callee } + +func (f *goFixInlineFuncFact) String() string { return "goFixInline " + f.Callee.String() } +func (*goFixInlineFuncFact) AFact() {} + +// A goFixInlineConstFact is exported for each constant marked "//go:fix inline". +// It holds information about an inlinable constant. Gob-serializable. +type goFixInlineConstFact struct { + // Information about "const LHSName = RHSName". + RHSName string + RHSPkgPath string + RHSPkgName string + rhsObj types.Object // for current package +} + +func (c *goFixInlineConstFact) String() string { + return fmt.Sprintf("goFixInline const %q.%s", c.RHSPkgPath, c.RHSName) +} + +func (*goFixInlineConstFact) AFact() {} + +// A goFixInlineAliasFact is exported for each type alias marked "//go:fix inline". +// It holds no information; its mere existence demonstrates that an alias is inlinable. +type goFixInlineAliasFact struct{} + +func (c *goFixInlineAliasFact) String() string { return "goFixInline alias" } +func (*goFixInlineAliasFact) AFact() {} + +func discard(string, ...any) {} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/internal/gofixdirective/gofixdirective.go b/vendor/golang.org/x/tools/go/analysis/passes/internal/gofixdirective/gofixdirective.go new file mode 100644 index 00000000..949df4bf --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/internal/gofixdirective/gofixdirective.go @@ -0,0 +1,143 @@ +// Copyright 2025 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 gofixdirective searches for and validates go:fix directives. The +// go/analysis/passes/inline package uses findgofix to perform inlining. +// The go/analysis/passes/gofix package uses findgofix to check for problems +// with go:fix directives. +// +// gofixdirective is separate from gofix to avoid depending on refactor/inline, +// which is large. +package gofixdirective + +// This package is tested by go/analysis/passes/inline. + +import ( + "go/ast" + "go/token" + "go/types" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/inspector" + internalastutil "golang.org/x/tools/internal/astutil" +) + +// A Handler handles language entities with go:fix directives. +type Handler interface { + HandleFunc(*ast.FuncDecl) + HandleAlias(*ast.TypeSpec) + HandleConst(name, rhs *ast.Ident) +} + +// Find finds functions and constants annotated with an appropriate "//go:fix" +// comment (the syntax proposed by #32816), and calls handler methods for each one. +// h may be nil. +func Find(pass *analysis.Pass, root inspector.Cursor, h Handler) { + for cur := range root.Preorder((*ast.FuncDecl)(nil), (*ast.GenDecl)(nil)) { + switch decl := cur.Node().(type) { + case *ast.FuncDecl: + findFunc(decl, h) + + case *ast.GenDecl: + if decl.Tok != token.CONST && decl.Tok != token.TYPE { + continue + } + declInline := hasFixInline(decl.Doc) + // Accept inline directives on the entire decl as well as individual specs. + for _, spec := range decl.Specs { + switch spec := spec.(type) { + case *ast.TypeSpec: // Tok == TYPE + findAlias(pass, spec, declInline, h) + + case *ast.ValueSpec: // Tok == CONST + findConst(pass, spec, declInline, h) + } + } + } + } +} + +func findFunc(decl *ast.FuncDecl, h Handler) { + if !hasFixInline(decl.Doc) { + return + } + if h != nil { + h.HandleFunc(decl) + } +} + +func findAlias(pass *analysis.Pass, spec *ast.TypeSpec, declInline bool, h Handler) { + if !declInline && !hasFixInline(spec.Doc) { + return + } + if !spec.Assign.IsValid() { + pass.Reportf(spec.Pos(), "invalid //go:fix inline directive: not a type alias") + return + } + + // Disallow inlines of type expressions containing array types. + // Given an array type like [N]int where N is a named constant, go/types provides + // only the value of the constant as an int64. So inlining A in this code: + // + // const N = 5 + // type A = [N]int + // + // would result in [5]int, breaking the connection with N. + for n := range ast.Preorder(spec.Type) { + if ar, ok := n.(*ast.ArrayType); ok && ar.Len != nil { + // Make an exception when the array length is a literal int. + if lit, ok := ast.Unparen(ar.Len).(*ast.BasicLit); ok && lit.Kind == token.INT { + continue + } + pass.Reportf(spec.Pos(), "invalid //go:fix inline directive: array types not supported") + return + } + } + if h != nil { + h.HandleAlias(spec) + } +} + +func findConst(pass *analysis.Pass, spec *ast.ValueSpec, declInline bool, h Handler) { + specInline := hasFixInline(spec.Doc) + if declInline || specInline { + for i, nameIdent := range spec.Names { + if i >= len(spec.Values) { + // Possible following an iota. + break + } + var rhsIdent *ast.Ident + switch val := spec.Values[i].(type) { + case *ast.Ident: + // Constants defined with the predeclared iota cannot be inlined. + if pass.TypesInfo.Uses[val] == builtinIota { + pass.Reportf(val.Pos(), "invalid //go:fix inline directive: const value is iota") + return + } + rhsIdent = val + case *ast.SelectorExpr: + rhsIdent = val.Sel + default: + pass.Reportf(val.Pos(), "invalid //go:fix inline directive: const value is not the name of another constant") + return + } + if h != nil { + h.HandleConst(nameIdent, rhsIdent) + } + } + } +} + +// hasFixInline reports the presence of a "//go:fix inline" directive +// in the comments. +func hasFixInline(cg *ast.CommentGroup) bool { + for _, d := range internalastutil.Directives(cg) { + if d.Tool == "go" && d.Name == "fix" && d.Args == "inline" { + return true + } + } + return false +} + +var builtinIota = types.Universe.Lookup("iota") diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/any.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/any.go index 579ab865..79e2802c 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/any.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/any.go @@ -18,7 +18,7 @@ var AnyAnalyzer = &analysis.Analyzer{ Doc: analyzerutil.MustExtractDoc(doc, "any"), Requires: []*analysis.Analyzer{inspect.Analyzer}, Run: runAny, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#any", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_any", } // The any pass replaces interface{} with go1.18's 'any'. diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go index 9df39fb2..6fd618f9 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/atomictypes.go @@ -33,7 +33,7 @@ var AtomicTypesAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: runAtomic, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#atomictypes", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_atomictypes", } // TODO(mkalil): support the Pointer variants. diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/bloop.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/bloop.go index ad45d744..d44c8567 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/bloop.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/bloop.go @@ -32,7 +32,7 @@ var BLoopAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: bloop, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#bloop", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_bloop", } // bloop updates benchmarks that use "for range b.N", replacing it diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go index 260dd509..caf3df95 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go @@ -15,13 +15,16 @@ causing build breakage. However, these problems are generally trivial to fix. We regard any modernizer whose fix changes program behavior to have a serious bug and will endeavor to fix it. -To apply all modernization fixes en masse, you can use the +Since Go 1.26, the 'go fix' command has included the modernize suite, +so to apply all modernization fixes en masse, you can use the following command: - $ go run golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest -fix ./... + $ go fix ./... + +If you need to run a modernizer added or modified since the Go +release, you can use this standalone command: -(Do not use "go get -tool" to add gopls as a dependency of your -module; gopls commands must be built from their release branch.) + $ go run golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest -fix ./... If the tool warns of conflicting fixes, you may need to run it more than once until it has applied all fixes cleanly. This command is @@ -182,6 +185,19 @@ unnecessary `x := x` statement. This fix only applies to `range` loops. +# Analyzer importcomment + +importcomment: remove obsolete comments specifying canonical import path + +The importcomment analyzer removes comments specifying the canonical +import path, such as + + package foo // import "example.com/foo" + +The go command enforced these comments in GOPATH mode via "go get", but +ignores them in module mode, so they are obsolete once the package +belongs to a module. The fix removes the comment. + # Analyzer mapsloop mapsloop: replace explicit loops over maps with calls to maps package @@ -331,6 +347,21 @@ No fix is offered in cases when the runtime type is dynamic, such as: or when the operand has potential side effects. +# Analyzer reflecttypeassert + +reflecttypeassert: replace v.Interface().(T) with reflect.TypeAssert[T](v) + +This analyzer suggests fixes to replace two-valued type assertions on +the result of (reflect.Value).Interface with reflect.TypeAssert, +introduced in go1.25, which avoids the intermediate allocation of an +interface value, for example: + + x, ok := v.Interface().(string) -> x, ok := reflect.TypeAssert[string](v) + +No fix is offered for single-valued assertions, since they panic when +the assertion fails whereas reflect.TypeAssert does not. Nor is a fix +offered for a type switch. + # Analyzer slicesbackward slicesbackward: replace backward loops over slices with slices.Backward @@ -353,6 +384,22 @@ the index and value variables are kept: for i, v := range slices.Backward(s) { ... } +# Analyzer slicesclip + +slicesclip: replace three-index slice expressions with slices.Clip + +The slicesclip analyzer suggests replacing a full slice expression of +the form + + x[:len(x):len(x)] + +which clips the capacity of a slice to its length, with the simpler +and more readable + + slices.Clip(x) + +added in Go 1.21. + # Analyzer slicescontains slicescontains: replace loops with slices.Contains or slices.ContainsFunc @@ -409,7 +456,7 @@ or its "for elem := range x.Len()" equivalent by a range loop over an iterator offered by the same data type: for elem := range x.All() { - use(x.At(i) + use(elem) } where x is one of various well-known types in the standard library. diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/embedlit.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/embedlit.go index da883d67..a30276a0 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/embedlit.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/embedlit.go @@ -33,7 +33,7 @@ var EmbedLitAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: runEmbedLit, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#embedlit", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_embedlit", } // Go1.27 introduced the ability to directly access embedded struct fields. @@ -84,14 +84,12 @@ func embedlitUnnest(pass *analysis.Pass, info *types.Info, curLit inspector.Curs // Can't promote an unkeyed field; would result in a syntax error. if kv, ok := elt.(*ast.KeyValueExpr); ok { if innerLit := isEmbeddedFieldLit(info, compLitType, kv); innerLit != nil { + // Inv: len(innerLit.Elts) > 0. We skip empty struct literals. // Emit edits to delete the unnecessary embedded field type specifier // and its closing brace. - closingPos := innerLit.Rbrace - if len(innerLit.Elts) > 0 { - // Delete any inner trailing commas or white space. Extra trailing commas - // would result in invalid code. - closingPos = innerLit.Elts[len(innerLit.Elts)-1].End() - } + // Delete any inner trailing commas or white space. Extra trailing commas + // would result in invalid code. + closingPos := innerLit.Elts[len(innerLit.Elts)-1].End() file := astutil.EnclosingFile(curLit) // Enable modernizer only for Go1.27. if !analyzerutil.FileUsesGoVersion(pass, file, versions.Go1_27) { @@ -132,11 +130,11 @@ func embedlitUnnest(pass *analysis.Pass, info *types.Info, curLit inspector.Curs } // We can safely delete the entire line if the key value expression is - // on a different line than the previous element, and the closing - // brace of the inner literal is on a different line than its opening - // brace. + // alone on its line: it starts on a new line relative to the previous + // element (prevLine < curLine), and the first element of the inner + // literal starts on a subsequent line. if prevLine < curLine && curLine < tokFile.LineCount() && // (1-based) - lineOf(innerLit.Lbrace) < lineOf(innerLit.Rbrace) { + lineOf(innerLit.Elts[0].Pos()) > curLine { lineStart := tokFile.LineStart(curLine) nextLineStart := tokFile.LineStart(curLine + 1) // Check that there are no comments on the line we are going to delete. @@ -211,15 +209,25 @@ func embedlitCombine(pass *analysis.Pass, index *typeindex.Index, info *types.In case edge.AssignStmt_Rhs: assign := curLit.Parent().Node().(*ast.AssignStmt) // TODO(mkalil): Handle lhs forms that aren't idents, i.e. x.y[i] = T{...}. - if id, ok := assign.Lhs[curLit.ParentEdgeIndex()].(*ast.Ident); ok { + // TODO(mkalil): Handle multi-assignments like t1, t2 := A{}, B{} + if len(assign.Lhs) != 1 { + return nil + } + if id, ok := assign.Lhs[0].(*ast.Ident); ok { lhs = id curStmt = curLit.Parent() } case edge.ValueSpec_Values: spec := curLit.Parent().Node().(*ast.ValueSpec) - lhs = spec.Names[curLit.ParentEdgeIndex()] + // TODO(mkalil): Handle multi-declarations like var (x = A{}; y = B{}) or var x, y = ... + if len(spec.Names) != 1 { + return nil + } + lhs = spec.Names[0] if decl, ok := moreiters.First(curLit.Enclosing((*ast.DeclStmt)(nil))); ok { - curStmt = decl + if gdecl, ok := decl.Node().(*ast.DeclStmt).Decl.(*ast.GenDecl); ok && len(gdecl.Specs) == 1 { + curStmt = decl + } } default: return nil @@ -233,7 +241,8 @@ func embedlitCombine(pass *analysis.Pass, index *typeindex.Index, info *types.In tObj = info.ObjectOf(lhs) // Marks the contiguous block of embedded field assign statements that will // be moved into the struct initialization. - firstStmt, lastStmt inspector.Cursor + firstStmt, lastStmt inspector.Cursor + hasEmbeddedSelection bool ) stmtloop: for { @@ -264,6 +273,15 @@ stmtloop: if obj != tObj { break } + // The selection is from an embedded field if it directly + // assigns an embedded struct field (t.B = B{...}) or if + // the length of the index path is greater than one. + seln := info.Selections[sel] + if v, ok := seln.Obj().(*types.Var); ok && v.Embedded() || + len(seln.Index()) > 1 { + hasEmbeddedSelection = true + } + rhsCur := curStmt.ChildAt(edge.AssignStmt_Rhs, 0) if uses(index, rhsCur, tObj) { break @@ -286,7 +304,8 @@ stmtloop: lastStmt = curStmt } - if !firstStmt.Valid() { + if !firstStmt.Valid() || !hasEmbeddedSelection { + // We should not suggest a fix if none of the selections are from embedded fields. return nil } diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go index 0e3f17fb..8a3f1c10 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/errorsastype.go @@ -27,7 +27,7 @@ import ( var ErrorsAsTypeAnalyzer = &analysis.Analyzer{ Name: "errorsastype", Doc: analyzerutil.MustExtractDoc(doc, "errorsastype"), - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#errorsastype", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_errorsastype", Requires: []*analysis.Analyzer{typeindexanalyzer.Analyzer}, Run: errorsastype, } @@ -228,6 +228,9 @@ func canUseErrorsAsType(info *types.Info, index *typeindex.Index, curCall inspec len(curDecl.Node().(*ast.GenDecl).Specs) != 1 { return // not a simple "var v T" decl } + if curDecl.ParentEdgeKind() != edge.DeclStmt_Decl { + return // package-level var, not a local declaration statement + } // AsType requires that its type argument implements error. // Reject if v does not implement error. if !types.AssignableTo(v.Type(), errorType) { @@ -239,5 +242,5 @@ func canUseErrorsAsType(info *types.Info, index *typeindex.Index, curCall inspec // ... // if errors.As(err, &v) { ... } // with no uses of v outside the IfStmt. - return v, curDecl.Parent(), curIfStmt // curDecl.Parent() is a DeclStmt + return v, curDecl.Parent(), curIfStmt } diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/fmtappendf.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/fmtappendf.go index 82106541..d67b6ad1 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/fmtappendf.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/fmtappendf.go @@ -30,7 +30,7 @@ var FmtAppendfAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: fmtappendf, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#fmtappendf", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_fmtappendf", } // The fmtappend function replaces []byte(fmt.Sprintf(...)) by diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/forvar.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/forvar.go index ba54daeb..d79f1f9f 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/forvar.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/forvar.go @@ -21,7 +21,7 @@ var ForVarAnalyzer = &analysis.Analyzer{ Doc: analyzerutil.MustExtractDoc(doc, "forvar"), Requires: []*analysis.Analyzer{inspect.Analyzer}, Run: forvar, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#forvar", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_forvar", } // forvar offers to fix unnecessary copying of a for variable diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/importcomment.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/importcomment.go new file mode 100644 index 00000000..de0f6879 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/importcomment.go @@ -0,0 +1,69 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modernize + +import ( + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/internal/analysis/analyzerutil" +) + +var importCommentAnalyzer = &analysis.Analyzer{ + Name: "importcomment", + Doc: analyzerutil.MustExtractDoc(doc, "importcomment"), + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_importcomment", + Run: importcomment, +} + +func importcomment(pass *analysis.Pass) (any, error) { + // Import path comments are ignored in module mode. + if pass.Module == nil { + return nil, nil + } + + for _, file := range pass.Files { + // An import comment follows the package name on the same line. + pkgEnd := file.Name.End() + pkgLine := pass.Fset.Position(pkgEnd).Line + for _, c := range file.Comments { + if len(c.List) != 1 { + continue + } + if c.Pos() < pkgEnd { + continue + } + commentLine := pass.Fset.Position(c.Pos()).Line + if commentLine > pkgLine { + break // comments are sorted; the rest are on later lines + } + // Have: package p // comment + if !isImportComment(c.Text()) { + continue + } + pass.Report(analysis.Diagnostic{ + Pos: c.Pos(), + End: c.End(), + Message: "canonical import path comment is ignored in module mode", + SuggestedFixes: []analysis.SuggestedFix{{ + Message: "Remove obsolete import path comment", + TextEdits: []analysis.TextEdit{{ + Pos: pkgEnd, // deletes the preceding space too + End: c.End(), + }}, + }}, + }) + } + } + + return nil, nil +} + +// isImportComment reports whether text, a comment's content with its +// markers removed, is a canonical import path comment, import "path". +func isImportComment(text string) bool { + text = strings.TrimSpace(text) + return strings.HasPrefix(text, `import "`) && strings.HasSuffix(text, `"`) +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/maps.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/maps.go index 7f3fd4e6..38d0f232 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/maps.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/maps.go @@ -28,7 +28,7 @@ var MapsLoopAnalyzer = &analysis.Analyzer{ Doc: analyzerutil.MustExtractDoc(doc, "mapsloop"), Requires: []*analysis.Analyzer{inspect.Analyzer}, Run: mapsloop, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#mapsloop", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_mapsloop", } // The mapsloop pass offers to simplify a loop of map insertions: diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go index 9fd86575..928fcf58 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/minmax.go @@ -32,7 +32,7 @@ var MinMaxAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: minmax, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#minmax", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_minmax", } // The minmax pass replaces if/else statements with calls to min or max, @@ -451,18 +451,3 @@ func checkMinMaxPattern(ifStmt *ast.IfStmt, falseResult ast.Expr, funcName, para // Check if the sign matches the function name return cond(sign < 0, "min", "max") == funcName } - -// -- utils -- - -func is[T any](x any) bool { - _, ok := x.(T) - return ok -} - -func cond[T any](cond bool, t, f T) T { - if cond { - return t - } else { - return f - } -} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go index d7dfe558..9e950d7c 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.go @@ -38,14 +38,17 @@ var Suite = []*analysis.Analyzer{ EmbedLitAnalyzer, ErrorsAsTypeAnalyzer, ForVarAnalyzer, + importCommentAnalyzer, // awaiting public symbol MapsLoopAnalyzer, MinMaxAnalyzer, NewExprAnalyzer, OmitZeroAnalyzer, PlusBuildAnalyzer, RangeIntAnalyzer, + reflectTypeAssertAnalyzer, // awaiting public symbol ReflectTypeForAnalyzer, slicesBackwardAnalyzer, // awaiting public symbol + slicesClipAnalyzer, // awaiting public symbol SlicesContainsAnalyzer, SlicesSortAnalyzer, StdIteratorsAnalyzer, @@ -121,7 +124,7 @@ func filesUsingGoVersion(pass *analysis.Pass, version string) iter.Seq[inspector // specified standard packages or their dependencies. func within(pass *analysis.Pass, pkgs ...string) bool { path := pass.Pkg.Path() - return packagepath.IsStdPackage(path) && + return packagepath.MaybeStdPackage(path) && moreiters.Contains(stdlib.Dependencies(pkgs...), path) } @@ -135,6 +138,7 @@ var ( builtinMake = types.Universe.Lookup("make") builtinNew = types.Universe.Lookup("new") builtinNil = types.Universe.Lookup("nil") + builtinRecover = types.Universe.Lookup("recover") builtinString = types.Universe.Lookup("string") builtinTrue = types.Universe.Lookup("true") byteSliceType = types.NewSlice(types.Typ[types.Byte]) @@ -184,3 +188,16 @@ func isLocal(obj types.Object) bool { } return depth >= 4 } + +func is[T any](x any) bool { + _, ok := x.(T) + return ok +} + +func cond[T any](cond bool, t, f T) T { + if cond { + return t + } else { + return f + } +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/newexpr.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/newexpr.go index c99d9c24..168c09fd 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/newexpr.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/newexpr.go @@ -24,7 +24,7 @@ import ( var NewExprAnalyzer = &analysis.Analyzer{ Name: "newexpr", Doc: analyzerutil.MustExtractDoc(doc, "newexpr"), - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#newexpr", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_newexpr", Requires: []*analysis.Analyzer{inspect.Analyzer}, Run: run, FactTypes: []analysis.Fact{&newLike{}}, @@ -53,7 +53,8 @@ func run(pass *analysis.Pass) (any, error) { if sig.Results().Len() == 1 && is[*types.Pointer](sig.Results().At(0).Type()) && // => no iface conversion sig.Params().Len() == 1 && - sig.Params().At(0) == v { + sig.Params().At(0) == v && + !sig.Variadic() { // we can't safely transform a variadic function call, so skip them // Export a fact for each one. pass.ExportObjectFact(fn, &newLike{}) diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/omitzero.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/omitzero.go index 59ba9506..4d93f7f4 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/omitzero.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/omitzero.go @@ -24,7 +24,7 @@ var OmitZeroAnalyzer = &analysis.Analyzer{ Doc: analyzerutil.MustExtractDoc(doc, "omitzero"), Requires: []*analysis.Analyzer{inspect.Analyzer}, Run: omitzero, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#omitzero", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_omitzero", } // The omitzero pass searches for instances of "omitempty" in a json field tag on a diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go index 574ce0a8..09877f89 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/plusbuild.go @@ -17,7 +17,7 @@ import ( var PlusBuildAnalyzer = &analysis.Analyzer{ Name: "plusbuild", Doc: analyzerutil.MustExtractDoc(doc, "plusbuild"), - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#plusbuild", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_plusbuild", Run: plusbuild, } diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go index f7cb965f..9cd76633 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/rangeint.go @@ -13,8 +13,6 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/edge" - "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/internal/analysis/analyzerutil" typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" @@ -33,7 +31,7 @@ var RangeIntAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: rangeint, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#rangeint", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_rangeint", } // rangeint offers a fix to replace a 3-clause 'for' loop: @@ -112,7 +110,7 @@ func rangeint(pass *analysis.Pass) (any, error) { // limit is a local or unexported global var. // (An exported global may have uses we can't see.) for cur := range typeindex.Uses(v) { - if isScalarLvalue(info, cur) { + if typesinternal.IsAssignedOrAddressTaken(info, cur) { // Limit var is assigned or address-taken. continue nextLoop } @@ -161,7 +159,7 @@ func rangeint(pass *analysis.Pass) (any, error) { // Reject if any is an l-value (assigned or address-taken): // a "for range int" loop does not respect assignments to // the loop variable. - if isScalarLvalue(info, curId) { + if typesinternal.IsAssignedOrAddressTaken(info, curId) { continue nextLoop } } @@ -345,40 +343,3 @@ func rangeint(pass *analysis.Pass) (any, error) { } return nil, nil } - -// isScalarLvalue reports whether the specified identifier is -// address-taken or appears on the left side of an assignment. -// -// This function is valid only for scalars (x = ...), -// not for aggregates (x.a[i] = ...) -func isScalarLvalue(info *types.Info, curId inspector.Cursor) bool { - // Unfortunately we can't simply use info.Types[e].Assignable() - // as it is always true for a variable even when that variable is - // used only as an r-value. So we must inspect enclosing syntax. - - cur := astutil.UnparenEnclosingCursor(curId) - - switch cur.ParentEdgeKind() { - case edge.AssignStmt_Lhs: - assign := cur.Parent().Node().(*ast.AssignStmt) - if assign.Tok != token.DEFINE { - return true // i = j or i += j - } - id := curId.Node().(*ast.Ident) - if v, ok := info.Defs[id]; ok && v.Pos() != id.Pos() { - return true // reassignment of i (i, j := 1, 2) - } - case edge.RangeStmt_Key: - rng := cur.Parent().Node().(*ast.RangeStmt) - if rng.Tok == token.ASSIGN { - return true // "for k, v = range x" is like an AssignStmt to k, v - } - case edge.IncDecStmt_X: - return true // i++, i-- - case edge.UnaryExpr_X: - if cur.Parent().Node().(*ast.UnaryExpr).Op == token.AND { - return true // &i - } - } - return false -} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go index 10fbdf8b..14446a13 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflect.go @@ -30,7 +30,7 @@ var ReflectTypeForAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: reflecttypefor, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#reflecttypefor", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_reflecttypefor", } func reflecttypefor(pass *analysis.Pass) (any, error) { diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflecttypeassert.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflecttypeassert.go new file mode 100644 index 00000000..ff41e43a --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/reflecttypeassert.go @@ -0,0 +1,118 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modernize + +import ( + "go/ast" + "go/token" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/edge" + "golang.org/x/tools/internal/analysis/analyzerutil" + typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" + "golang.org/x/tools/internal/astutil" + "golang.org/x/tools/internal/refactor" + "golang.org/x/tools/internal/typesinternal" + "golang.org/x/tools/internal/typesinternal/typeindex" + "golang.org/x/tools/internal/versions" +) + +var reflectTypeAssertAnalyzer = &analysis.Analyzer{ + Name: "reflecttypeassert", + Doc: analyzerutil.MustExtractDoc(doc, "reflecttypeassert"), + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + typeindexanalyzer.Analyzer, + }, + Run: reflecttypeassert, + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_reflecttypeassert", +} + +func reflecttypeassert(pass *analysis.Pass) (any, error) { + var ( + index = pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index) + info = pass.TypesInfo + + valueInterface = index.Selection("reflect", "Value", "Interface") + ) + + for curCall := range index.Calls(valueInterface) { + call := curCall.Node().(*ast.CallExpr) + // Have: v.Interface() + + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + continue // method expression reflect.Value.Interface(v) + } + + // TypeAssert's argument must be a reflect.Value; a pointer + // receiver would need an explicit dereference in the rewrite. + if !typesinternal.IsTypeNamed(info.TypeOf(sel.X), "reflect", "Value") { + continue + } + + // The call must be the operand of a type assertion + // (not a type switch, whose Type field is nil). + curOperand := astutil.UnparenEnclosingCursor(curCall) + if curOperand.ParentEdgeKind() != edge.TypeAssertExpr_X { + continue + } + curAssert := curOperand.Parent() + assert := curAssert.Node().(*ast.TypeAssertExpr) + if assert.Type == nil { + continue // type switch + } + + // The assertion must be the sole RHS of a two-valued + // assignment, x, ok := v.Interface().(T), so that the + // rewrite preserves the "commaOK" semantics; a single-valued + // assertion panics on failure whereas TypeAssert does not. + curRhs := astutil.UnparenEnclosingCursor(curAssert) + if curRhs.ParentEdgeKind() != edge.AssignStmt_Rhs { + continue + } + assign := curRhs.Parent().Node().(*ast.AssignStmt) + if len(assign.Lhs) != 2 || len(assign.Rhs) != 1 || + (assign.Tok != token.ASSIGN && assign.Tok != token.DEFINE) { + continue + } + + file := astutil.EnclosingFile(curCall) + if !analyzerutil.FileUsesGoVersion(pass, file, versions.Go1_25) { + continue // TypeAssert requires go1.25 + } + + prefix, importEdits := refactor.AddImport(info, file, "reflect", "reflect", "TypeAssert", assert.Pos()) + + tstr := astutil.Format(pass.Fset, assert.Type) + pass.Report(analysis.Diagnostic{ + Pos: assert.Pos(), + End: assert.End(), + Message: "Interface().(" + tstr + ") can be simplified using reflect.TypeAssert", + SuggestedFixes: []analysis.SuggestedFix{{ + // v.Interface().(T) -> reflect.TypeAssert[T](v) + Message: "Replace Interface().(" + tstr + ") by reflect.TypeAssert[" + tstr + "]", + // Edit around sel.X instead of reformatting it, so its + // comments and spacing are preserved; only the type, + // which must move, is reformatted. + TextEdits: append(importEdits, + analysis.TextEdit{ + Pos: assert.Pos(), + End: sel.X.Pos(), + NewText: []byte(prefix + "TypeAssert[" + tstr + "]("), + }, + analysis.TextEdit{ + Pos: sel.X.End(), + End: assert.End(), + NewText: []byte(")"), + }, + ), + }}, + }) + } + + return nil, nil +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/slices.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/slices.go index 6c8ea22b..4fb40a89 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/slices.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/slices.go @@ -27,7 +27,7 @@ var AppendClippedAnalyzer = &analysis.Analyzer{ Doc: analyzerutil.MustExtractDoc(doc, "appendclipped"), Requires: []*analysis.Analyzer{inspect.Analyzer}, Run: appendclipped, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#appendclipped", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_appendclipped", } // The appendclipped pass offers to simplify a tower of append calls: diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesbackward.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesbackward.go index 02cd30a2..c65a6c7e 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesbackward.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesbackward.go @@ -19,6 +19,7 @@ import ( typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex" "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/refactor" + "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal/typeindex" "golang.org/x/tools/internal/versions" ) @@ -32,7 +33,7 @@ var slicesBackwardAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: slicesbackward, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#slicesbackward", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_slicesbackward", } // slicesbackward offers a fix to replace a manually-written backward loop: @@ -128,7 +129,7 @@ func slicesbackward(pass *analysis.Pass) (any, error) { // (e.g. &i before the loop). bodyCur := curLoop.Child(loop.Body) for curUse := range index.Uses(indexObj) { - if !isScalarLvalue(info, curUse) { + if !typesinternal.IsAssignedOrAddressTaken(info, curUse) { continue } if bodyCur.Contains(curUse) { @@ -160,20 +161,25 @@ func slicesbackward(pass *analysis.Pass) (any, error) { // If so, we also need to check whether s[i] is an lvalue. If we're // mutating the slice or taking an element's address, a fix will not // be offered. + // Modernization to "for _, v := range slices.Backward(s)" is unsafe if + // s[i] is mutated or address-taken (since v would be a local copy of + // the element so s[i] wouldn't get mutated). + // We don't need to worry about indirect selections (e.g. s[i].n++ where + // s is []*item) or indirect references like indexing a slice of slices. if curUse.ParentEdgeKind() == edge.IndexExpr_Index { - if isScalarLvalue(pass.TypesInfo, curUse.Parent()) { + curIdx := curUse.Parent() + if typesinternal.IsAssignedOrAddressTaken(info, curIdx) { continue nextLoop } - idxCur := curUse.Parent() - idxExpr := idxCur.Node().(*ast.IndexExpr) + idxExpr := curIdx.Node().(*ast.IndexExpr) if astutil.EqualSyntax(idxExpr.X, sliceExpr) { sliceIdxs++ // If the current statement is the first in the body of the form // "name := s[i]", save it so we can use "name" as the value // variable in slices.Backward. We can also remove the entire assign // statement. - if firstSliceIdxAssign == nil && idxCur.ParentEdgeKind() == edge.AssignStmt_Rhs { - assignStmt := idxCur.Parent().Node().(*ast.AssignStmt) + if firstSliceIdxAssign == nil && curIdx.ParentEdgeKind() == edge.AssignStmt_Rhs { + assignStmt := curIdx.Parent().Node().(*ast.AssignStmt) if len(assignStmt.Lhs) == 1 && assignStmt.Tok == token.DEFINE { // The condition above implies that assignStmt.Lhs[0] is a valid // identifier. diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesclip.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesclip.go new file mode 100644 index 00000000..b08cbc69 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesclip.go @@ -0,0 +1,81 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modernize + +import ( + "fmt" + "go/ast" + "go/types" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysis/analyzerutil" + "golang.org/x/tools/internal/astutil" + "golang.org/x/tools/internal/refactor" + "golang.org/x/tools/internal/typesinternal" + "golang.org/x/tools/internal/versions" +) + +var slicesClipAnalyzer = &analysis.Analyzer{ + Name: "slicesclip", + Doc: analyzerutil.MustExtractDoc(doc, "slicesclip"), + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + }, + Run: slicesclip, + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_slicesclip", +} + +func slicesclip(pass *analysis.Pass) (any, error) { + if within(pass, "slices", "runtime") { + return nil, nil + } + info := pass.TypesInfo + + // isLenX reports whether e is a call len(x) where x is + // syntactically identical to the operand x of the slice expr. + isLenX := func(e, x ast.Expr) bool { + call, ok := e.(*ast.CallExpr) + if !ok || len(call.Args) != 1 { + return false + } + return typeutil.Callee(info, call) == builtinLen && + astutil.EqualSyntax(call.Args[0], x) + } + + for curFile := range filesUsingGoVersion(pass, versions.Go1_21) { + file := curFile.Node().(*ast.File) + + for curSlice := range curFile.Preorder((*ast.SliceExpr)(nil)) { + slice := curSlice.Node().(*ast.SliceExpr) + _, ok := info.TypeOf(slice.X).Underlying().(*types.Slice) // in case x is an array/pointer to array + if !slice.Slice3 || slice.Low != nil || !ok { + continue + } + + if isLenX(slice.High, slice.X) && isLenX(slice.Max, slice.X) && typesinternal.NoEffects(info, slice.X) { + // Have x[:len(x):len(x)] -> slices.Clip(x) + prefix, edits := refactor.AddImport(info, file, "slices", "slices", "Clip", slice.Pos()) + sx := astutil.Format(pass.Fset, slice.X) + pass.Report(analysis.Diagnostic{ + Pos: slice.Pos(), + End: slice.End(), + Message: "x[:len(x):len(x)] can be simplified using slices.Clip", + SuggestedFixes: []analysis.SuggestedFix{{ + Message: fmt.Sprintf("Replace with slices.Clip(%s)", sx), + TextEdits: append(edits, analysis.TextEdit{ + Pos: slice.Pos(), + End: slice.End(), + NewText: fmt.Appendf(nil, "%sClip(%s)", prefix, sx), + }), + }}, + }) + } + } + } + + return nil, nil +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go index ed75e05e..b27be6fc 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicescontains.go @@ -32,7 +32,7 @@ var SlicesContainsAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: slicescontains, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#slicescontains", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_slicescontains", } // The slicescontains pass identifies loops that can be replaced by a diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesdelete.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesdelete.go index 7b3aa875..c623aede 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesdelete.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/slicesdelete.go @@ -25,7 +25,7 @@ var SlicesDeleteAnalyzer = &analysis.Analyzer{ Doc: analyzerutil.MustExtractDoc(doc, "slicesdelete"), Requires: []*analysis.Analyzer{inspect.Analyzer}, Run: slicesdelete, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#slicesdelete", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_slicesdelete", } // The slicesdelete pass attempts to replace instances of append(s[:i], s[i+k:]...) diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/sortslice.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/sortslice.go index e22b8c55..08d86676 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/sortslice.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/sortslice.go @@ -28,7 +28,7 @@ var SlicesSortAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: slicessort, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#slicessort", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_slicessort", } // The slicessort pass replaces sort.Slice(slice, less) with diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go index 19532686..25208863 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/stditerators.go @@ -28,7 +28,7 @@ var StdIteratorsAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: stditerators, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stditerators", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_stditerators", } // stditeratorsTable records std types that have legacy T.{Len,At} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go index 6aa9c881..ca1cc149 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsbuilder.go @@ -34,7 +34,7 @@ var StringsBuilderAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: stringsbuilder, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stringbuilder", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_stringsbuilder", } // stringsbuilder replaces string += string in a loop by strings.Builder. diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go index ae93d4d3..daa6a678 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscut.go @@ -35,7 +35,7 @@ var StringsCutAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: stringscut, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stringscut", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_stringscut", } // stringscut offers a fix to replace an occurrence of strings.Index{,Byte} with @@ -513,9 +513,12 @@ func indexArgValid(info *types.Info, index *typeindex.Index, expr ast.Expr, afte info.Types[expr.Fun].IsType() && // make sure this isn't a function that returns a byte slice indexArgValid(info, index, expr.Args[0], afterPos) // check s in []byte(s) case *ast.Ident: - sObj := info.Uses[expr] - sUses := index.Uses(sObj) - return !hasModifyingUses(sUses, afterPos) + for use := range index.Uses(info.Uses[expr]) { + if typesinternal.IsAssignedOrAddressTaken(info, use) { + return false + } + } + return true default: // For now, skip instances where s or substr are not // identifiers, basic lits, or call expressions of the form @@ -612,30 +615,6 @@ func checkIdxUses(info *types.Info, uses iter.Seq[inspector.Cursor], s, substr a return negative, nonnegative, beforeSlice, afterSlice } -// hasModifyingUses reports whether any of the uses involve potential -// modifications. Uses involving assignments before the "afterPos" won't be -// considered. -func hasModifyingUses(uses iter.Seq[inspector.Cursor], afterPos token.Pos) bool { - for curUse := range uses { - ek := curUse.ParentEdgeKind() - if ek == edge.AssignStmt_Lhs { - if curUse.Node().Pos() <= afterPos { - continue - } - // Any use on the LHS is a modifying use. - return true - } else if ek == edge.UnaryExpr_X && - curUse.Parent().Node().(*ast.UnaryExpr).Op == token.AND { - // Modifying use because we might be passing the object by reference (an explicit &). - // We can ignore the case where we have a method call on the expression (which - // has an implicit &) because we know the type of s and substr are strings - // which cannot have methods on them. - return true - } - } - return false -} - // checkIdxComparison reports whether the check is equivalent to i < 0 or its negation, or neither. // For equivalent to i >= 0, we only accept this exact BinaryExpr since // expressions like i > 0 or i >= 1 make a stronger statement about the value of i. diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscutprefix.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscutprefix.go index 11d33590..6e285456 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscutprefix.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringscutprefix.go @@ -31,7 +31,7 @@ var StringsCutPrefixAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: stringscutprefix, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stringscutprefix", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_stringscutprefix", } // stringscutprefix offers a fix to replace an if statement which diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsseq.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsseq.go index d02a5323..064444dc 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsseq.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/stringsseq.go @@ -28,7 +28,7 @@ var StringsSeqAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: stringsseq, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#stringsseq", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_stringsseq", } // stringsseq offers a fix to replace a call to strings.Split with @@ -117,6 +117,8 @@ func stringsseq(pass *analysis.Pass) (any, error) { } switch obj := typeutil.Callee(info, call); obj { + case nil: + // a conversion, not a call case stringsSplit, stringsFields, bytesSplit, bytesFields: oldFnName := obj.Name() seqFnName := fmt.Sprintf("%sSeq", oldFnName) diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/testingcontext.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/testingcontext.go index 93933052..da723647 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/testingcontext.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/testingcontext.go @@ -33,7 +33,7 @@ var TestingContextAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: testingContext, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#testingcontext", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_testingcontext", } // The testingContext pass replaces calls to context.WithCancel from within diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/unsafefuncs.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/unsafefuncs.go index 34c135ca..08198c9b 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/unsafefuncs.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/unsafefuncs.go @@ -34,7 +34,7 @@ var unsafeFuncsAnalyzer = &analysis.Analyzer{ Doc: analyzerutil.MustExtractDoc(doc, "unsafefuncs"), Requires: []*analysis.Analyzer{inspect.Analyzer}, Run: unsafefuncs, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#unsafefuncs", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_unsafefuncs", } func unsafefuncs(pass *analysis.Pass) (any, error) { diff --git a/vendor/golang.org/x/tools/go/analysis/passes/modernize/waitgroupgo.go b/vendor/golang.org/x/tools/go/analysis/passes/modernize/waitgroupgo.go index 9af2d3bd..4bc88d0f 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/modernize/waitgroupgo.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/modernize/waitgroupgo.go @@ -9,6 +9,7 @@ import ( "fmt" "go/ast" "go/printer" + "go/types" "slices" "golang.org/x/tools/go/analysis" @@ -30,7 +31,7 @@ var WaitGroupGoAnalyzer = &analysis.Analyzer{ typeindexanalyzer.Analyzer, }, Run: waitgroup, - URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#waitgroupgo", + URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#hdr-Analyzer_waitgroupgo", } // The waitgroupgo pass replaces old more complex code with @@ -112,7 +113,7 @@ func waitgroup(pass *analysis.Pass) (any, error) { astutil.EqualSyntax(ast.Unparen(deferStmt.Call.Fun).(*ast.SelectorExpr).X, addCallRecv) { doneStmt = deferStmt // "defer wg.Done()" - } else if lastStmt, ok := list[len(list)-1].(*ast.ExprStmt); ok { + } else if lastStmt, ok := list[len(list)-1].(*ast.ExprStmt); ok && cannotRecover(lit.Body, info) { if doneCall, ok := lastStmt.X.(*ast.CallExpr); ok && typeutil.Callee(info, doneCall) == syncWaitGroupDone && astutil.EqualSyntax(ast.Unparen(doneCall.Fun).(*ast.SelectorExpr).X, addCallRecv) { @@ -175,3 +176,44 @@ func waitgroup(pass *analysis.Pass) (any, error) { } return nil, nil } + +// cannotRecover reports whether no panic arising in body can be +// recovered. It conservatively treats a defer of anything but a +// recover-free function literal (e.g. a named function) as able to recover. +func cannotRecover(body *ast.BlockStmt, info *types.Info) bool { + res := true + ast.Inspect(body, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.DeferStmt: + lit, ok := ast.Unparen(n.Call.Fun).(*ast.FuncLit) + if !ok || containsRecover(lit.Body, info) { + res = false + } + // Each defer is fully handled here; don't descend into it. + return false + case *ast.FuncLit: + // Defers in nested functions cannot recover panics from this body. + return false + } + return true + }) + return res +} + +func containsRecover(body *ast.BlockStmt, info *types.Info) bool { + found := false + ast.Inspect(body, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.CallExpr: + if typeutil.Callee(info, n) == builtinRecover { + found = true + return false + } + case *ast.FuncLit: + // Recover calls in nested functions cannot recover panics from body. + return false + } + return true + }) + return found +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go b/vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go index 6f353968..18a100a8 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go @@ -7,6 +7,7 @@ package nilness import ( _ "embed" "fmt" + "go/ast" "go/token" "go/types" @@ -14,6 +15,7 @@ import ( "golang.org/x/tools/go/analysis/passes/buildssa" "golang.org/x/tools/go/ssa" "golang.org/x/tools/internal/analysis/analyzerutil" + "golang.org/x/tools/internal/astutil" "golang.org/x/tools/internal/typeparams" ) @@ -37,6 +39,14 @@ func run(pass *analysis.Pass) (any, error) { } func runFunc(pass *analysis.Pass, fn *ssa.Function) { + // Skip cgo-generated functions annotated with + // //go:cgo_unsafe_args such as _cgo_cmalloc since + // they behave in magical ways not captured by the + // SSA representation, leading to false positives. + if hasCgoUnsafeArgs(fn) { + return + } + reportf := func(category string, pos token.Pos, format string, args ...any) { // We ignore nil-checking ssa.Instructions // that don't correspond to syntax. @@ -495,3 +505,17 @@ func isRangeIndex(instr *ssa.IndexAddr) bool { } return false } + +func hasCgoUnsafeArgs(fn *ssa.Function) bool { + for ; fn != nil; fn = fn.Parent() { + if decl, ok := fn.Syntax().(*ast.FuncDecl); ok && decl != nil { + for _, d := range astutil.Directives(decl.Doc) { + if d.Tool == "go" && d.Name == "cgo_unsafe_args" { + return true + } + } + } + + } + return false +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go b/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go index f82d2eae..6573c3fc 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go @@ -1184,15 +1184,6 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, name string) { } } } - if strings.HasSuffix(name, "ln") { - // The last item, if a string, should not have a newline. - arg = args[len(args)-1] - if s, ok := stringConstantExpr(pass, arg); ok { - if strings.HasSuffix(s, "\n") { - pass.ReportRangef(call, "%s arg list ends with redundant newline", name) - } - } - } for _, arg := range args { if isFunctionValue(pass, arg) { pass.ReportRangef(call, "%s arg %s is a func value, not called", name, astutil.Format(pass.Fset, arg)) diff --git a/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go b/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go index d1fda880..3d44c4e8 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go @@ -15,6 +15,7 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/stdlib" "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/versions" ) @@ -64,8 +65,8 @@ func run(pass *analysis.Pass) (any, error) { pkg *types.Package version string } - memo := make(map[key]map[types.Object]string) // records symbol's minimum Go version - disallowedSymbols := func(pkg *types.Package, version string) map[types.Object]string { + memo := make(map[key]map[types.Object]stdlib.Symbol) + disallowedSymbols := func(pkg *types.Package, version string) map[types.Object]stdlib.Symbol { k := key{pkg, version} disallowed, ok := memo[k] if !ok { @@ -98,20 +99,12 @@ func run(pass *analysis.Pass) (any, error) { if fileVersion != "" { if obj, ok := pass.TypesInfo.Uses[n]; ok && obj.Pkg() != nil { disallowed := disallowedSymbols(obj.Pkg(), fileVersion) - if minVersion, ok := disallowed[origin(obj)]; ok { - // Some symbols are accessible before their release but - // only with specific build tags unknown to us here. - // Avoid false positives in such cases. - // TODO(mkalil): move this check into typesinternal.TooNewStdSymbols. - if obj.Pkg().Path() == "testing/synctest" && versions.AtLeast(fileVersion, "go1.24") { - break // requires go1.24 && goexperiment.synctest || go1.25 - } - noun := "module" - if fileVersion != pkgVersion { - noun = "file" - } + if sym, ok := disallowed[origin(obj)]; ok { pass.ReportRangef(n, "%s.%s requires %v or later (%s is %s)", - obj.Pkg().Name(), obj.Name(), minVersion, noun, fileVersion) + obj.Pkg().Name(), sym.Name, + sym.Version, + cond(fileVersion != pkgVersion, "file", "module"), + fileVersion) } } } @@ -134,3 +127,11 @@ func origin(obj types.Object) types.Object { } return obj } + +func cond[T any](cond bool, t, f T) T { + if cond { + return t + } else { + return f + } +} diff --git a/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go b/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go index bd32d586..82968f46 100644 --- a/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go +++ b/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go @@ -16,7 +16,6 @@ package unusedresult import ( _ "embed" "go/ast" - "go/token" "go/types" "sort" "strings" @@ -27,6 +26,7 @@ import ( "golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/internal/analysis/analyzerutil" "golang.org/x/tools/internal/astutil" + "golang.org/x/tools/internal/typesinternal" ) //go:embed doc.go @@ -172,7 +172,7 @@ func run(pass *analysis.Pass) (any, error) { } // func() string -var sigNoArgsStringResult = types.NewSignatureType(nil, nil, nil, nil, types.NewTuple(types.NewParam(token.NoPos, nil, "", types.Typ[types.String])), false) +var sigNoArgsStringResult = types.NewSignatureType(nil, nil, nil, nil, typesinternal.TupleOf(types.Typ[types.String]), false) type stringSetFlag map[string]bool diff --git a/vendor/golang.org/x/tools/go/ast/inspector/cursor.go b/vendor/golang.org/x/tools/go/ast/inspector/cursor.go index 239b10c4..1c482252 100644 --- a/vendor/golang.org/x/tools/go/ast/inspector/cursor.go +++ b/vendor/golang.org/x/tools/go/ast/inspector/cursor.go @@ -10,6 +10,7 @@ import ( "go/token" "iter" "reflect" + "strings" "golang.org/x/tools/go/ast/edge" ) @@ -110,6 +111,46 @@ func (c Cursor) String() string { return reflect.TypeOf(c.Node()).String() } +// GoString returns a string describing the cursor's path from the +// root, if any. +func (c Cursor) GoString() string { + if !c.Valid() { + return "(invalid)" + } + if c.index < 0 { + return "(root)" + } + // e.g "File.Decls[1].(*ast.GenDecl).Specs[0].(*ast.TypeSpec)" + // + // In hindsight even the File node should have reported a + // virtual ParentEdge of (Root_Files, i) where i is the index + // among the files passed to NewInspector. Then the path would + // read "(root).Files[i]", etc; but we missed the boat. + var buf strings.Builder + buf.WriteString("File") + var visit func(Cursor) + visit = func(c Cursor) { + ek, idx := c.ParentEdge() + if ek == edge.Invalid { + return // File + } + visit(c.Parent()) + fmt.Fprintf(&buf, ".%s", ek.FieldName()) + if idx >= 0 { + fmt.Fprintf(&buf, "[%d]", idx) + } + ftype := ek.FieldType() + if idx >= 0 { + ftype = ftype.Elem() // []T -> T + } + if ftype.Kind() == reflect.Interface { + fmt.Fprintf(&buf, ".(%T)", c.Node()) + } + } + visit(c) + return buf.String() +} + // indices return the [start, end) half-open interval of event indices. func (c Cursor) indices() (int32, int32) { if c.index < 0 { diff --git a/vendor/golang.org/x/tools/go/loader/loader.go b/vendor/golang.org/x/tools/go/loader/loader.go index 9c5f7db1..3c9d4fe7 100644 --- a/vendor/golang.org/x/tools/go/loader/loader.go +++ b/vendor/golang.org/x/tools/go/loader/loader.go @@ -738,7 +738,9 @@ func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.Fil // Preprocess CgoFiles and parse the outputs (sequentially). if which == 'g' && bp.CgoFiles != nil { + ioLimit <- true cgofiles, err := cgo.ProcessFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode) + <-ioLimit if err != nil { errs = append(errs, err) } else { diff --git a/vendor/golang.org/x/tools/go/packages/visit.go b/vendor/golang.org/x/tools/go/packages/visit.go index c546b1b6..06747a9d 100644 --- a/vendor/golang.org/x/tools/go/packages/visit.go +++ b/vendor/golang.org/x/tools/go/packages/visit.go @@ -5,11 +5,11 @@ package packages import ( - "cmp" "fmt" "iter" "os" - "slices" + + "golang.org/x/tools/internal/moremaps" ) // Visit visits all the packages in the import graph whose roots are @@ -40,7 +40,7 @@ func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) { seen[pkg] = true if pre == nil || pre(pkg) { - for _, imp := range sorted(pkg.Imports) { // for determinism + for _, imp := range moremaps.Sorted(pkg.Imports) { // for determinism visit(imp) } } @@ -88,7 +88,7 @@ func Postorder(pkgs []*Package) iter.Seq[*Package] { visit = func(pkg *Package) bool { if !seen[pkg] { seen[pkg] = true - for _, imp := range sorted(pkg.Imports) { // for determinism + for _, imp := range moremaps.Sorted(pkg.Imports) { // for determinism if !visit(imp) { return false } @@ -106,28 +106,3 @@ func Postorder(pkgs []*Package) iter.Seq[*Package] { } } } - -// -- copied from golang.org.x/tools/gopls/internal/util/moremaps -- - -// sorted returns an iterator over the entries of m in key order. -func sorted[M ~map[K]V, K cmp.Ordered, V any](m M) iter.Seq2[K, V] { - // TODO(adonovan): use maps.Sorted if proposal #68598 is accepted. - return func(yield func(K, V) bool) { - keys := keySlice(m) - slices.Sort(keys) - for _, k := range keys { - if !yield(k, m[k]) { - break - } - } - } -} - -// KeySlice returns the keys of the map M, like slices.Collect(maps.Keys(m)). -func keySlice[M ~map[K]V, K comparable, V any](m M) []K { - r := make([]K, 0, len(m)) - for k := range m { - r = append(r, k) - } - return r -} diff --git a/vendor/golang.org/x/tools/go/ssa/builder.go b/vendor/golang.org/x/tools/go/ssa/builder.go index 1669d80b..a663af8f 100644 --- a/vendor/golang.org/x/tools/go/ssa/builder.go +++ b/vendor/golang.org/x/tools/go/ssa/builder.go @@ -85,6 +85,7 @@ import ( "slices" "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/versions" ) @@ -124,7 +125,7 @@ var ( // The ssa:deferstack intrinsic returns the current function's defer stack. vDeferStack = &Builtin{ name: "ssa:deferstack", - sig: types.NewSignatureType(nil, nil, nil, nil, types.NewTuple(anonVar(tDeferStack)), false), + sig: types.NewSignatureType(nil, nil, nil, nil, typesinternal.TupleOf(tDeferStack), false), } ) @@ -1719,7 +1720,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) { for _, st := range states { if st.Dir == types.RecvOnly { chtyp := typeparams.CoreType(fn.typ(st.Chan.Type())).(*types.Chan) - vars = append(vars, anonVar(chtyp.Elem())) + vars = append(vars, newVar("", chtyp.Elem())) } } sel.setType(types.NewTuple(vars...)) diff --git a/vendor/golang.org/x/tools/go/ssa/const.go b/vendor/golang.org/x/tools/go/ssa/const.go index 91ed6f28..73a8213a 100644 --- a/vendor/golang.org/x/tools/go/ssa/const.go +++ b/vendor/golang.org/x/tools/go/ssa/const.go @@ -131,7 +131,9 @@ func nillable(t types.Type) bool { return u != nil && nillable(u) }) } - switch t.Underlying().(type) { + switch t := t.Underlying().(type) { + case *types.Basic: + return t.Kind() == types.UnsafePointer case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature: return true case *types.Interface: diff --git a/vendor/golang.org/x/tools/go/ssa/methods.go b/vendor/golang.org/x/tools/go/ssa/methods.go index 82faadeb..8de0785c 100644 --- a/vendor/golang.org/x/tools/go/ssa/methods.go +++ b/vendor/golang.org/x/tools/go/ssa/methods.go @@ -167,10 +167,18 @@ func (prog *Program) RuntimeTypes() []types.Type { // eliminates the need to eagerly compute all the element // types during SSA building. var runtimeTypes []types.Type - add := func(t types.Type) { runtimeTypes = append(runtimeTypes, t) } var set typeutil.Map // for de-duping identical types for t := range prog.makeInterfaceTypes { - typesinternal.ForEachElement(&set, &prog.MethodSets, t, add) + typesinternal.ForEachElement(prog.MethodSets.MethodSet, t, func(t types.Type, access bool) bool { + if !access { + return false // inaccessible to reflection + } + seen, _ := set.Set(t, true).(bool) + if !seen { + runtimeTypes = append(runtimeTypes, t) + } + return seen + }) } return runtimeTypes diff --git a/vendor/golang.org/x/tools/go/ssa/util.go b/vendor/golang.org/x/tools/go/ssa/util.go index 42f9621c..5dfca720 100644 --- a/vendor/golang.org/x/tools/go/ssa/util.go +++ b/vendor/golang.org/x/tools/go/ssa/util.go @@ -181,19 +181,13 @@ func newVar(name string, typ types.Type) *types.Var { return types.NewParam(token.NoPos, nil, name, typ) } -// anonVar creates an anonymous 'var' for use in a types.Tuple. -func anonVar(typ types.Type) *types.Var { - return newVar("", typ) -} - -var lenResults = types.NewTuple(anonVar(tInt)) +var lenResults = typesinternal.TupleOf(tInt) // makeLen returns the len builtin specialized to type func(T)int. func makeLen(T types.Type) *Builtin { - lenParams := types.NewTuple(anonVar(T)) return &Builtin{ name: "len", - sig: types.NewSignatureType(nil, nil, nil, lenParams, lenResults, false), + sig: types.NewSignatureType(nil, nil, nil, typesinternal.TupleOf(T), lenResults, false), } } diff --git a/vendor/golang.org/x/tools/go/ssa/wrappers.go b/vendor/golang.org/x/tools/go/ssa/wrappers.go index 6cadd049..fbb06743 100644 --- a/vendor/golang.org/x/tools/go/ssa/wrappers.go +++ b/vendor/golang.org/x/tools/go/ssa/wrappers.go @@ -26,6 +26,7 @@ import ( "go/types" "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/internal/typesinternal" ) // -- wrappers ----------------------------------------------------------- @@ -118,10 +119,12 @@ func (b *builder) buildWrapper(fn *Function) { // For simple indirection wrappers, perform an informative nil-check: // "value method (T).f called using nil *T pointer" if len(indices) == 1 && !isPointer(recvType(fn.object)) { + params := typesinternal.TupleOf(fn.method.recv, tString, tString) + results := typesinternal.TupleOf(fn.method.recv) var c Call c.Call.Value = &Builtin{ name: "ssa:wrapnilchk", - sig: types.NewSignatureType(nil, nil, nil, types.NewTuple(anonVar(fn.method.recv), anonVar(tString), anonVar(tString)), types.NewTuple(anonVar(fn.method.recv)), false), + sig: types.NewSignatureType(nil, nil, nil, params, results, false), } c.Call.Args = []Value{ v, diff --git a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go index 0d6d0bce..9c7401c9 100644 --- a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go +++ b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go @@ -30,6 +30,7 @@ import ( "slices" "strconv" "strings" + "sync" "golang.org/x/tools/internal/typesinternal" ) @@ -126,7 +127,8 @@ func For(obj types.Object) (Path, error) { // An Encoder amortizes the cost of encoding the paths of multiple objects. // The zero value of an Encoder is ready to use. type Encoder struct { - pkgIndex map[*types.Package]*pkgIndex + pkgIndexMu sync.Mutex + pkgIndex map[*types.Package]*pkgIndex } // A traversal encapsulates the state of a single traversal of the object/type graph. @@ -191,6 +193,8 @@ type pkgIndex struct { // For returns the path to an object relative to its package, // or an error if the object is not accessible from the package's Scope. // +// For is safe for concurrent use. +// // The For function guarantees to return a path only for the following objects: // - package-level types // - exported package-level non-types @@ -320,6 +324,9 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { panic(obj) } + enc.pkgIndexMu.Lock() + defer enc.pkgIndexMu.Unlock() + // 4. Search the object/type graph for the path to // the var (field/param/result) or method. ix, ok := enc.pkgIndex[pkg] diff --git a/vendor/golang.org/x/tools/go/types/typeutil/callee.go b/vendor/golang.org/x/tools/go/types/typeutil/callee.go index 3d24a8c6..b64a8f45 100644 --- a/vendor/golang.org/x/tools/go/types/typeutil/callee.go +++ b/vendor/golang.org/x/tools/go/types/typeutil/callee.go @@ -7,7 +7,8 @@ package typeutil import ( "go/ast" "go/types" - _ "unsafe" // for linkname + + "golang.org/x/tools/internal/typesinternal" ) // Callee returns the named target of a function call, if any: @@ -19,14 +20,7 @@ import ( // Note: for calls of instantiated functions and methods, Callee returns // the corresponding generic function or method on the generic type. func Callee(info *types.Info, call *ast.CallExpr) types.Object { - obj := info.Uses[usedIdent(info, call.Fun)] - if obj == nil { - return nil - } - if _, ok := obj.(*types.TypeName); ok { - return nil - } - return obj + return typesinternal.Callee(info, call) } // StaticCallee returns the target (function or method) of a static function @@ -35,52 +29,5 @@ func Callee(info *types.Info, call *ast.CallExpr) types.Object { // Note: for calls of instantiated functions and methods, StaticCallee returns // the corresponding generic function or method on the generic type. func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func { - obj := info.Uses[usedIdent(info, call.Fun)] - fn, _ := obj.(*types.Func) - if fn == nil || interfaceMethod(fn) { - return nil - } - return fn -} - -// usedIdent is the implementation of [internal/typesinternal.UsedIdent]. -// It returns the identifier associated with e. -// See typesinternal.UsedIdent for a fuller description. -// This function should live in typesinternal, but cannot because it would -// create an import cycle. -// -//go:linkname usedIdent golang.org/x/tools/go/types/typeutil.usedIdent -func usedIdent(info *types.Info, e ast.Expr) *ast.Ident { - if info.Types == nil || info.Uses == nil { - panic("one of info.Types or info.Uses is nil; both must be populated") - } - // Look through type instantiation if necessary. - switch d := ast.Unparen(e).(type) { - case *ast.IndexExpr: - if info.Types[d.Index].IsType() { - e = d.X - } - case *ast.IndexListExpr: - e = d.X - } - - switch e := ast.Unparen(e).(type) { - // info.Uses always has the object we want, even for selector expressions. - // We don't need info.Selections. - // See go/types/recording.go:recordSelection. - case *ast.Ident: - return e - case *ast.SelectorExpr: - return e.Sel - } - return nil -} - -// interfaceMethod reports whether its argument is a method of an interface. -// This function should live in typesinternal, but cannot because it would create an import cycle. -// -//go:linkname interfaceMethod golang.org/x/tools/go/types/typeutil.interfaceMethod -func interfaceMethod(f *types.Func) bool { - recv := f.Signature().Recv() - return recv != nil && types.IsInterface(recv.Type()) + return typesinternal.StaticCallee(info, call) } diff --git a/vendor/golang.org/x/tools/internal/analysis/analyzerutil/version.go b/vendor/golang.org/x/tools/internal/analysis/analyzerutil/version.go index 700d53ef..60d7253c 100644 --- a/vendor/golang.org/x/tools/internal/analysis/analyzerutil/version.go +++ b/vendor/golang.org/x/tools/internal/analysis/analyzerutil/version.go @@ -38,7 +38,7 @@ func FileUsesGoVersion(pass *analysis.Pass, file *ast.File, version string) (_re // The bootstrap rule does not cover tests, // and some tests (e.g. debug/elf/file_test.go) rely on this. pkgpath := pass.Pkg.Path() - if packagepath.IsStdPackage(pkgpath) && + if packagepath.MaybeStdPackage(pkgpath) && stdlib.IsBootstrapPackage(pkgpath) && // (excludes "*_test" external test packages) !strings.HasSuffix(pass.Fset.File(file.Pos()).Name(), "_test.go") { // (excludes all tests) fileVersion = stdlib.BootstrapVersion.String() // package must bootstrap diff --git a/vendor/golang.org/x/tools/internal/astutil/free/free.go b/vendor/golang.org/x/tools/internal/astutil/free/free.go new file mode 100644 index 00000000..2c4d2c4e --- /dev/null +++ b/vendor/golang.org/x/tools/internal/astutil/free/free.go @@ -0,0 +1,418 @@ +// Copyright 2025 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 free defines utilities for computing the free variables of +// a syntax tree without type information. This is inherently +// heuristic because of the T{f: x} ambiguity, in which f may or may +// not be a lexical reference depending on whether T is a struct type. +package free + +import ( + "go/ast" + "go/token" +) + +// Copied, with considerable changes, from go/parser/resolver.go +// at af53bd2c03. + +// Names computes an approximation to the set of free names of the AST +// at node n based solely on syntax. +// +// In the absence of composite literals, the set of free names is exact. Composite +// literals introduce an ambiguity that can only be resolved with type information: +// whether F is a field name or a value in `T{F: ...}`. +// If includeComplitIdents is true, this function conservatively assumes +// T is not a struct type, so freeishNames overapproximates: the resulting +// set may contain spurious entries that are not free lexical references +// but are references to struct fields. +// If includeComplitIdents is false, this function assumes that T *is* +// a struct type, so freeishNames underapproximates: the resulting set +// may omit names that are free lexical references. +// +// TODO(adonovan): includeComplitIdents is a crude hammer: the caller +// may have partial or heuristic information about whether a given T +// is struct type. Replace includeComplitIdents with a hook to query +// the caller. +// +// The code is based on go/parser.resolveFile, but heavily simplified. Crucial +// differences are: +// - Instead of resolving names to their objects, this function merely records +// whether they are free. +// - Labels are ignored: they do not refer to values. +// - This is never called on ImportSpecs, so the function panics if it sees one. +func Names(n ast.Node, includeComplitIdents bool) map[string]bool { + v := &freeVisitor{ + free: make(map[string]bool), + includeComplitIdents: includeComplitIdents, + } + // Begin with a scope, even though n might not be a form that establishes a scope. + // For example, n might be: + // x := ... + // Then we need to add the first x to some scope. + v.openScope() + ast.Walk(v, n) + v.closeScope() + if v.scope != nil { + panic("unbalanced scopes") + } + return v.free +} + +// A freeVisitor holds state for a free-name analysis. +type freeVisitor struct { + scope *scope // the current innermost scope + free map[string]bool // free names seen so far + includeComplitIdents bool // include identifier key in composite literals +} + +// scope contains all the names defined in a lexical scope. +// It is like ast.Scope, but without deprecation warnings. +type scope struct { + names map[string]bool + outer *scope +} + +func (s *scope) defined(name string) bool { + for ; s != nil; s = s.outer { + if s.names[name] { + return true + } + } + return false +} + +func (v *freeVisitor) Visit(n ast.Node) ast.Visitor { + switch n := n.(type) { + + // Expressions. + case *ast.Ident: + v.use(n) + + case *ast.FuncLit: + v.openScope() + defer v.closeScope() + v.walkFuncType(nil, n.Type) + v.walkBody(n.Body) + + case *ast.SelectorExpr: + v.walk(n.X) + // Skip n.Sel: it cannot be free. + + case *ast.StructType: + v.openScope() + defer v.closeScope() + v.walkFieldList(n.Fields) + + case *ast.FuncType: + v.openScope() + defer v.closeScope() + v.walkFuncType(nil, n) + + case *ast.CompositeLit: + v.walk(n.Type) + for _, e := range n.Elts { + if kv, _ := e.(*ast.KeyValueExpr); kv != nil { + if ident, _ := kv.Key.(*ast.Ident); ident != nil { + // It is not possible from syntax alone to know whether + // an identifier used as a composite literal key is + // a struct field (if n.Type is a struct) or a value + // (if n.Type is a map, slice or array). + if v.includeComplitIdents { + // Over-approximate by treating both cases as potentially + // free names. + v.use(ident) + } else { + // Under-approximate by ignoring potentially free names. + } + } else { + v.walk(kv.Key) + } + v.walk(kv.Value) + } else { + v.walk(e) + } + } + + case *ast.InterfaceType: + v.openScope() + defer v.closeScope() + v.walkFieldList(n.Methods) + + // Statements + case *ast.AssignStmt: + walkSlice(v, n.Rhs) + if n.Tok == token.DEFINE { + v.shortVarDecl(n.Lhs) + } else { + walkSlice(v, n.Lhs) + } + + case *ast.LabeledStmt: + // Ignore labels. + v.walk(n.Stmt) + + case *ast.BranchStmt: + // Ignore labels. + + case *ast.BlockStmt: + v.openScope() + defer v.closeScope() + walkSlice(v, n.List) + + case *ast.IfStmt: + v.openScope() + defer v.closeScope() + v.walk(n.Init) + v.walk(n.Cond) + v.walk(n.Body) + v.walk(n.Else) + + case *ast.CaseClause: + walkSlice(v, n.List) + v.openScope() + defer v.closeScope() + walkSlice(v, n.Body) + + case *ast.SwitchStmt: + v.openScope() + defer v.closeScope() + v.walk(n.Init) + v.walk(n.Tag) + v.walkBody(n.Body) + + case *ast.TypeSwitchStmt: + v.openScope() + defer v.closeScope() + if n.Init != nil { + v.walk(n.Init) + } + v.walk(n.Assign) + // We can use walkBody here because we don't track label scopes. + v.walkBody(n.Body) + + case *ast.CommClause: + v.openScope() + defer v.closeScope() + v.walk(n.Comm) + walkSlice(v, n.Body) + + case *ast.SelectStmt: + v.walkBody(n.Body) + + case *ast.ForStmt: + v.openScope() + defer v.closeScope() + v.walk(n.Init) + v.walk(n.Cond) + v.walk(n.Post) + v.walk(n.Body) + + case *ast.RangeStmt: + v.openScope() + defer v.closeScope() + v.walk(n.X) + var lhs []ast.Expr + if n.Key != nil { + lhs = append(lhs, n.Key) + } + if n.Value != nil { + lhs = append(lhs, n.Value) + } + if len(lhs) > 0 { + if n.Tok == token.DEFINE { + v.shortVarDecl(lhs) + } else { + walkSlice(v, lhs) + } + } + v.walk(n.Body) + + // Declarations + case *ast.GenDecl: + switch n.Tok { + case token.CONST, token.VAR: + for _, spec := range n.Specs { + spec := spec.(*ast.ValueSpec) + walkSlice(v, spec.Values) + v.walk(spec.Type) + v.declare(spec.Names...) + } + + case token.TYPE: + for _, spec := range n.Specs { + spec := spec.(*ast.TypeSpec) + // Go spec: The scope of a type identifier declared inside a + // function begins at the identifier in the TypeSpec and ends + // at the end of the innermost containing block. + v.declare(spec.Name) + if spec.TypeParams != nil { + v.openScope() + defer v.closeScope() + v.walkTypeParams(spec.TypeParams) + } + v.walk(spec.Type) + } + + case token.IMPORT: + panic("encountered import declaration in free analysis") + } + + case *ast.FuncDecl: + if n.Recv == nil && n.Name.Name != "init" { // package-level function + v.declare(n.Name) + } + v.openScope() + defer v.closeScope() + v.walkTypeParams(n.Type.TypeParams) + v.walkFuncType(n.Recv, n.Type) + v.walkBody(n.Body) + + default: + return v + } + + return nil +} + +func (v *freeVisitor) openScope() { + v.scope = &scope{map[string]bool{}, v.scope} +} + +func (v *freeVisitor) closeScope() { + v.scope = v.scope.outer +} + +func (v *freeVisitor) walk(n ast.Node) { + if n != nil { + ast.Walk(v, n) + } +} + +func (v *freeVisitor) walkFuncType(recv *ast.FieldList, typ *ast.FuncType) { + // First use field types... + v.walkRecvFieldType(recv) + v.walkFieldTypes(typ.Params) + v.walkFieldTypes(typ.Results) + + // ...then declare field names. + v.declareFieldNames(recv) + v.declareFieldNames(typ.Params) + v.declareFieldNames(typ.Results) +} + +// A receiver field is not like a param or result field because +// "func (recv R[T]) method()" uses R but declares T. +func (v *freeVisitor) walkRecvFieldType(list *ast.FieldList) { + if list == nil { + return + } + for _, f := range list.List { // valid => len=1 + typ := f.Type + if ptr, ok := typ.(*ast.StarExpr); ok { + typ = ptr.X + } + + // Analyze receiver type as Base[Index, ...] + var ( + base ast.Expr + indices []ast.Expr + ) + switch typ := typ.(type) { + case *ast.IndexExpr: // B[T] + base, indices = typ.X, []ast.Expr{typ.Index} + case *ast.IndexListExpr: // B[K, V] + base, indices = typ.X, typ.Indices + default: // B + base = typ + } + for _, expr := range indices { + if id, ok := expr.(*ast.Ident); ok { + v.declare(id) + } + } + v.walk(base) + } +} + +// walkTypeParams is like walkFieldList, but declares type parameters eagerly so +// that they may be resolved in the constraint expressions held in the field +// Type. +func (v *freeVisitor) walkTypeParams(list *ast.FieldList) { + v.declareFieldNames(list) + v.walkFieldTypes(list) // constraints +} + +func (v *freeVisitor) walkBody(body *ast.BlockStmt) { + if body == nil { + return + } + walkSlice(v, body.List) +} + +func (v *freeVisitor) walkFieldList(list *ast.FieldList) { + if list == nil { + return + } + v.walkFieldTypes(list) // .Type may contain references + v.declareFieldNames(list) // .Names declares names +} + +func (v *freeVisitor) shortVarDecl(lhs []ast.Expr) { + // Go spec: A short variable declaration may redeclare variables provided + // they were originally declared in the same block with the same type, and + // at least one of the non-blank variables is new. + // + // However, it doesn't matter to free analysis whether a variable is declared + // fresh or redeclared. + for _, x := range lhs { + // In a well-formed program each expr must be an identifier, + // but be forgiving. + if id, ok := x.(*ast.Ident); ok { + v.declare(id) + } + } +} + +func walkSlice[S ~[]E, E ast.Node](r *freeVisitor, list S) { + for _, e := range list { + r.walk(e) + } +} + +// walkFieldTypes resolves the types of the walkFieldTypes in list. +// The companion method declareFieldList declares the names of the walkFieldTypes. +func (v *freeVisitor) walkFieldTypes(list *ast.FieldList) { + if list != nil { + for _, f := range list.List { + v.walk(f.Type) + } + } +} + +// declareFieldNames declares the names of the fields in list. +// (Names in a FieldList always establish new bindings.) +// The companion method resolveFieldList resolves the types of the fields. +func (v *freeVisitor) declareFieldNames(list *ast.FieldList) { + if list != nil { + for _, f := range list.List { + v.declare(f.Names...) + } + } +} + +// use marks ident as free if it is not in scope. +func (v *freeVisitor) use(ident *ast.Ident) { + if s := ident.Name; s != "_" && !v.scope.defined(s) { + v.free[s] = true + } +} + +// declare adds each non-blank ident to the current scope. +func (v *freeVisitor) declare(idents ...*ast.Ident) { + for _, id := range idents { + if id.Name != "_" { + v.scope.names[id.Name] = true + } + } +} diff --git a/vendor/golang.org/x/tools/internal/imports/fix.go b/vendor/golang.org/x/tools/internal/imports/fix.go index b99ea6c9..6aa1e27e 100644 --- a/vendor/golang.org/x/tools/internal/imports/fix.go +++ b/vendor/golang.org/x/tools/internal/imports/fix.go @@ -32,6 +32,7 @@ import ( "golang.org/x/tools/internal/event" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gopathwalk" + "golang.org/x/tools/internal/modindex" "golang.org/x/tools/internal/stdlib" ) @@ -320,6 +321,7 @@ func (p *pass) importIdentifier(imp *ImportInfo) string { // load reads in everything necessary to run a pass, and reports whether the // file already has all the imports it needs. It fills in p.missingRefs with the // file's missing symbols, if any, or removes unused imports if not. +// This is called 3(!) times: self, otherFiles, loadRealPackageNames func (p *pass) load(ctx context.Context) ([]*ImportFix, bool) { p.knownPackages = map[string]*PackageInfo{} p.missingRefs = References{} @@ -577,6 +579,17 @@ func getFixes(ctx context.Context, fset *token.FileSet, f *ast.File, filename st } func getFixesWithSource(ctx context.Context, fset *token.FileSet, f *ast.File, filename string, goroot string, logf func(string, ...any), source Source) ([]*ImportFix, error) { + // If there is an Index for the GOMODCACHE, remember that, and later make it so that the + // directory walk doesn't go into the module cache, since we already have all the information + var ix *modindex.Index + src, ok := source.(*ProcessEnvSource) + if ok { + var err error + if ix, err = modindex.Read(src.env.Env["GOMODCACHE"]); err != nil { + ix = nil // don't use it if there was an error + } + } + // This logic is defensively duplicated from getFixes. abs, err := filepath.Abs(filename) if err != nil { @@ -635,6 +648,20 @@ func getFixesWithSource(ctx context.Context, fset *token.FileSet, f *ast.File, f } p.loadRealPackageNames = true p.otherFiles = otherFiles + if ix != nil { + src, ok := p.source.(*ProcessEnvSource) + if ok { + // For safety, clone the env so that we don't modify the caller's env. + env := *src.env + env.Env = maps.Clone(src.env.Env) + src.env = &env + // avoid looking in the module cache, as we have the index instead: + // This makes a later call to newModuleresolver (from + // LoadPackageNames) produce a resolver that will not look + // in the module cache + src.env.Env["GOMODCACHE"] = "" + } + } if fixes, done := p.load(ctx); done { return fixes, nil } @@ -649,7 +676,7 @@ func getFixesWithSource(ctx context.Context, fset *token.FileSet, f *ast.File, f // 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(ctx, p, p.missingRefs, filename); err != nil { + if err := addExternalCandidates(ctx, p, p.missingRefs, filename, ix); err != nil { return nil, err } @@ -1184,7 +1211,7 @@ type scanCallback struct { exportsLoaded func(pkg *pkg, exports []stdlib.Symbol) } -func addExternalCandidates(ctx context.Context, pass *pass, refs References, filename string) error { +func addExternalCandidates(ctx context.Context, pass *pass, refs References, filename string, ix *modindex.Index) error { ctx, done := event.Start(ctx, "imports.addExternalCandidates") defer done() @@ -1193,6 +1220,24 @@ func addExternalCandidates(ctx context.Context, pass *pass, refs References, fil return err } + // Add candidates from the module cache. + if ix != nil { + for k, v := range refs { + for n := range v { + cands := ix.Lookup(k, n, false) + for _, cand := range cands { + x := &Result{ + &ImportInfo{ImportPath: cand.ImportPath}, + &PackageInfo{Name: cand.PkgName, + Exports: map[string]bool{cand.Name: true}, + }, + } + results = append(results, x) + } + } + } + } + for _, result := range results { if result == nil { continue diff --git a/vendor/golang.org/x/tools/internal/imports/mod.go b/vendor/golang.org/x/tools/internal/imports/mod.go index 76fae6a7..a05faf97 100644 --- a/vendor/golang.org/x/tools/internal/imports/mod.go +++ b/vendor/golang.org/x/tools/internal/imports/mod.go @@ -166,10 +166,7 @@ func newModuleResolver(e *ProcessEnv, moduleCacheCache *DirInfoCache) (*ModuleRe } } - r.moduleCacheDir = gomodcacheForEnv(goenv) - if r.moduleCacheDir == "" { - return nil, fmt.Errorf("cannot resolve GOMODCACHE") - } + r.moduleCacheDir = goenv["GOMODCACHE"] sort.Slice(r.modsByModPath, func(i, j int) bool { count := func(x int) int { @@ -238,26 +235,6 @@ func newModuleResolver(e *ProcessEnv, moduleCacheCache *DirInfoCache) (*ModuleRe return r, nil } -// gomodcacheForEnv returns the GOMODCACHE value to use based on the given env -// map, which must have GOMODCACHE and GOPATH populated. -// -// TODO(rfindley): this is defensive refactoring. -// 1. Is this even relevant anymore? Can't we just read GOMODCACHE. -// 2. Use this to separate module cache scanning from other scanning. -func gomodcacheForEnv(goenv map[string]string) string { - if gmc := goenv["GOMODCACHE"]; gmc != "" { - // golang/go#67156: ensure that the module cache is clean, since it is - // assumed as a prefix to directories scanned by gopathwalk, which are - // themselves clean. - return filepath.Clean(gmc) - } - gopaths := filepath.SplitList(goenv["GOPATH"]) - if len(gopaths) == 0 { - return "" - } - return filepath.Join(gopaths[0], "/pkg/mod") -} - func (r *ModuleResolver) initAllMods() error { stdout, err := r.env.invokeGo(context.TODO(), "list", "-m", "-e", "-json", "...") if err != nil { diff --git a/vendor/golang.org/x/tools/internal/imports/source_env.go b/vendor/golang.org/x/tools/internal/imports/source_env.go index ec996c3c..272ae806 100644 --- a/vendor/golang.org/x/tools/internal/imports/source_env.go +++ b/vendor/golang.org/x/tools/internal/imports/source_env.go @@ -53,7 +53,7 @@ func (s *ProcessEnvSource) ResolveReferences(ctx context.Context, filename strin found := make(map[string][]pkgDistance) callback := &scanCallback{ rootFound: func(gopathwalk.Root) bool { - return true // We want everything. + return true }, dirFound: func(pkg *pkg) bool { return pkgIsCandidate(filename, refs, pkg) diff --git a/vendor/golang.org/x/tools/internal/modindex/directories.go b/vendor/golang.org/x/tools/internal/modindex/directories.go new file mode 100644 index 00000000..9a963744 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/modindex/directories.go @@ -0,0 +1,131 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modindex + +import ( + "fmt" + "log" + "os" + "path/filepath" + "regexp" + "strings" + "sync" + "time" + + "golang.org/x/mod/semver" + "golang.org/x/tools/internal/gopathwalk" +) + +type directory struct { + path string // relative to GOMODCACHE + importPath string + version string // semantic version +} + +// bestDirByImportPath returns the best directory for each import +// path, where "best" means most recent semantic version. These import +// paths are inferred from the GOMODCACHE-relative dir names in dirs. +func bestDirByImportPath(dirs []string) (map[string]directory, error) { + dirsByPath := make(map[string]directory) + for _, dir := range dirs { + importPath, version, err := dirToImportPathVersion(dir) + if err != nil { + return nil, err + } + new := directory{ + path: dir, + importPath: importPath, + version: version, + } + if old, ok := dirsByPath[importPath]; !ok || compareDirectory(new, old) < 0 { + dirsByPath[importPath] = new + } + } + return dirsByPath, nil +} + +// compareDirectory defines an ordering of path@version directories, +// by descending version, then by ascending path. +func compareDirectory(x, y directory) int { + if sign := -semver.Compare(x.version, y.version); sign != 0 { + return sign // latest first + } + return strings.Compare(string(x.path), string(y.path)) +} + +// modCacheRegexp splits a relpathpath into module, module version, and package. +var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) + +// dirToImportPathVersion computes import path and semantic version +// from a GOMODCACHE-relative directory name. +func dirToImportPathVersion(dir string) (string, string, error) { + m := modCacheRegexp.FindStringSubmatch(string(dir)) + // m[1] is the module path + // m[2] is the version major.minor.patch(-
 1 && flds[1][1] == 'D',
+			}
+			if px.Type == Func {
+				n, err := strconv.Atoi(flds[2])
+				if err != nil {
+					continue // should never happen
+				}
+				px.Results = int16(n)
+				if len(flds) >= 4 {
+					sig := strings.Split(flds[3], " ")
+					for i := range sig {
+						// $ cannot otherwise occur. removing the spaces
+						// almost works, but for chan struct{}, e.g.
+						sig[i] = strings.Replace(sig[i], "$", " ", -1)
+					}
+					px.Sig = toFields(sig)
+				}
+			}
+			ans = append(ans, px)
+		}
+	}
+	return ans
+}
+
+func toFields(sig []string) []Field {
+	ans := make([]Field, len(sig)/2)
+	for i := range ans {
+		ans[i] = Field{Arg: sig[2*i], Type: sig[2*i+1]}
+	}
+	return ans
+}
+
+// benchmarks show this is measurably better than strings.Split
+// split into first 4 fields separated by single space
+func fastSplit(x string) []string {
+	ans := make([]string, 0, 4)
+	nxt := 0
+	start := 0
+	for i := 0; i < len(x); i++ {
+		if x[i] != ' ' {
+			continue
+		}
+		ans = append(ans, x[start:i])
+		nxt++
+		start = i + 1
+		if nxt >= 3 {
+			break
+		}
+	}
+	ans = append(ans, x[start:])
+	return ans
+}
+
+func asLexType(c byte) LexType {
+	switch c {
+	case 'C':
+		return Const
+	case 'V':
+		return Var
+	case 'T':
+		return Type
+	case 'F':
+		return Func
+	}
+	return -1
+}
diff --git a/vendor/golang.org/x/tools/internal/modindex/modindex.go b/vendor/golang.org/x/tools/internal/modindex/modindex.go
new file mode 100644
index 00000000..16a7cabb
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/modindex/modindex.go
@@ -0,0 +1,121 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package modindex contains code for building and searching an
+// [Index] of the Go module cache.
+package modindex
+
+// The directory containing the index, returned by
+// [IndexDir], contains a file index-name- that contains the name
+// of the current index. We believe writing that short file is atomic.
+// [Read] reads that file to get the file name of the index.
+// WriteIndex writes an index with a unique name and then
+// writes that name into a new version of index-name-.
+// ( stands for the CurrentVersion of the index format.)
+
+import (
+	"maps"
+	"os"
+	"path/filepath"
+	"slices"
+	"strings"
+	"time"
+
+	"golang.org/x/mod/semver"
+)
+
+// Update updates the index for the specified Go
+// module cache directory, creating it as needed.
+// On success it returns the current index.
+func Update(gomodcache string) (*Index, error) {
+	prev, err := Read(gomodcache)
+	if err != nil {
+		if !os.IsNotExist(err) {
+			return nil, err
+		}
+		prev = nil
+	}
+	return update(gomodcache, prev)
+}
+
+// update builds, writes, and returns the current index.
+//
+// If old is nil, the new index is built from all of GOMODCACHE;
+// otherwise it is built from the old index plus cache updates
+// since the previous index's time.
+func update(gomodcache string, old *Index) (*Index, error) {
+	gomodcache, err := filepath.Abs(gomodcache)
+	if err != nil {
+		return nil, err
+	}
+	new, changed, err := build(gomodcache, old)
+	if err != nil {
+		return nil, err
+	}
+	if old == nil || changed {
+		if err := write(gomodcache, new); err != nil {
+			return nil, err
+		}
+	}
+	return new, nil
+}
+
+// build returns a new index for the specified Go module cache (an
+// absolute path).
+//
+// If an old index is provided, only directories more recent than it
+// that it are scanned; older directories are provided by the old
+// Index.
+//
+// The boolean result indicates whether new entries were found.
+func build(gomodcache string, old *Index) (*Index, bool, error) {
+	// Set the time window.
+	var start time.Time // = dawn of time
+	if old != nil {
+		// -1s to accommodate skew between (fine-grained) time.Now
+		// and (coarse) file system's mtime clock.
+		start = old.ValidAt.Add(-1 * time.Second)
+	}
+	now := time.Now()
+	end := now.Add(24 * time.Hour) // safely in the future
+
+	// Enumerate GOMODCACHE package directories.
+	// Choose the best (latest) package for each import path.
+	pkgDirs := findDirs(gomodcache, start, end)
+	dirByPath, err := bestDirByImportPath(pkgDirs)
+	if err != nil {
+		return nil, false, err
+	}
+
+	// For each import path it might occur only in
+	// dirByPath, only in old, or in both.
+	// If both, use the semantically later one.
+	var entries []Entry
+	if old != nil {
+		for _, entry := range old.Entries {
+			dir, ok := dirByPath[entry.ImportPath]
+			if !ok || semver.Compare(dir.version, entry.Version) <= 0 {
+				// New dir is missing or not more recent; use old entry.
+				entries = append(entries, entry)
+				delete(dirByPath, entry.ImportPath)
+			}
+		}
+	}
+
+	// Extract symbol information for all the new directories.
+	newEntries := extractSymbols(gomodcache, maps.Values(dirByPath))
+	entries = append(entries, newEntries...)
+	slices.SortFunc(entries, func(x, y Entry) int {
+		if n := strings.Compare(x.PkgName, y.PkgName); n != 0 {
+			return n
+		}
+		return strings.Compare(x.ImportPath, y.ImportPath)
+	})
+
+	return &Index{
+		GOMODCACHE: gomodcache,
+		ValidAt:    now, // time before the directories were scanned
+		Entries:    entries,
+	}, len(newEntries) > 0, nil
+}
diff --git a/vendor/golang.org/x/tools/internal/modindex/symbols.go b/vendor/golang.org/x/tools/internal/modindex/symbols.go
new file mode 100644
index 00000000..8e9702d8
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/modindex/symbols.go
@@ -0,0 +1,244 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modindex
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"iter"
+	"os"
+	"path/filepath"
+	"runtime"
+	"slices"
+	"strings"
+	"sync"
+
+	"golang.org/x/sync/errgroup"
+)
+
+// The name of a symbol contains information about the symbol:
+//  T for types, TD if the type is deprecated
+//  C for consts, CD if the const is deprecated
+//  V for vars, VD if the var is deprecated
+// and for funcs:  F  ( )*
+// any spaces in  are replaced by $s so that the fields
+// of the name are space separated. F is replaced by FD if the func
+// is deprecated.
+type symbol struct {
+	pkg  string // name of the symbols's package
+	name string // declared name
+	kind string // T, C, V, or F, followed by D if deprecated
+	sig  string // signature information, for F
+}
+
+// extractSymbols returns a (new, unordered) array of Entries, one for
+// each provided package directory, describing its exported symbols.
+func extractSymbols(cwd string, dirs iter.Seq[directory]) []Entry {
+	var (
+		mu      sync.Mutex
+		entries []Entry
+	)
+
+	var g errgroup.Group
+	g.SetLimit(max(2, runtime.GOMAXPROCS(0)/2))
+	for dir := range dirs {
+		g.Go(func() error {
+			thedir := filepath.Join(cwd, string(dir.path))
+			mode := parser.SkipObjectResolution | parser.ParseComments
+
+			// Parse all Go files in dir and extract symbols.
+			dirents, err := os.ReadDir(thedir)
+			if err != nil {
+				return nil // log this someday?
+			}
+			var syms []symbol
+			for _, dirent := range dirents {
+				if !strings.HasSuffix(dirent.Name(), ".go") ||
+					strings.HasSuffix(dirent.Name(), "_test.go") {
+					continue
+				}
+				fname := filepath.Join(thedir, dirent.Name())
+				tr, err := parser.ParseFile(token.NewFileSet(), fname, nil, mode)
+				if err != nil {
+					continue // ignore errors, someday log them?
+				}
+				syms = append(syms, getFileExports(tr)...)
+			}
+
+			// Create an entry for the package.
+			pkg, names := processSyms(syms)
+			if pkg != "" {
+				mu.Lock()
+				defer mu.Unlock()
+				entries = append(entries, Entry{
+					PkgName:    pkg,
+					Dir:        dir.path,
+					ImportPath: dir.importPath,
+					Version:    dir.version,
+					Names:      names,
+				})
+			}
+
+			return nil
+		})
+	}
+	g.Wait() // ignore error
+
+	return entries
+}
+
+func getFileExports(f *ast.File) []symbol {
+	pkg := f.Name.Name
+	if pkg == "main" || pkg == "" {
+		return nil
+	}
+	var ans []symbol
+	// should we look for //go:build ignore?
+	for _, decl := range f.Decls {
+		switch decl := decl.(type) {
+		case *ast.FuncDecl:
+			if decl.Recv != nil {
+				// ignore methods, as we are completing package selections
+				continue
+			}
+			name := decl.Name.Name
+			dtype := decl.Type
+			// not looking at dtype.TypeParams. That is, treating
+			// generic functions just like non-generic ones.
+			sig := dtype.Params
+			kind := "F"
+			if isDeprecated(decl.Doc) {
+				kind += "D"
+			}
+			result := []string{fmt.Sprintf("%d", dtype.Results.NumFields())}
+			for _, x := range sig.List {
+				// This code creates a string representing the type.
+				// TODO(pjw): it may be fragile:
+				// 1. x.Type could be nil, perhaps in ill-formed code
+				// 2. ExprString might someday change incompatibly to
+				//    include struct tags, which can be arbitrary strings
+				if x.Type == nil {
+					// Can this happen without a parse error? (Files with parse
+					// errors are ignored in getSymbols)
+					continue // maybe report this someday
+				}
+				tp := types.ExprString(x.Type)
+				if len(tp) == 0 {
+					// Can this happen?
+					continue // maybe report this someday
+				}
+				// This is only safe if ExprString never returns anything with a $
+				// The only place a $ can occur seems to be in a struct tag, which
+				// can be an arbitrary string literal, and ExprString does not presently
+				// print struct tags. So for this to happen the type of a formal parameter
+				// has to be a explicit struct, e.g. foo(x struct{a int "$"}) and ExprString
+				// would have to show the struct tag. Even testing for this case seems
+				// a waste of effort, but let's remember the possibility
+				if strings.Contains(tp, "$") {
+					continue
+				}
+				tp = strings.Replace(tp, " ", "$", -1)
+				if len(x.Names) == 0 {
+					result = append(result, "_")
+					result = append(result, tp)
+				} else {
+					for _, y := range x.Names {
+						result = append(result, y.Name)
+						result = append(result, tp)
+					}
+				}
+			}
+			sigs := strings.Join(result, " ")
+			if s := newsym(pkg, name, kind, sigs); s != nil {
+				ans = append(ans, *s)
+			}
+		case *ast.GenDecl:
+			depr := isDeprecated(decl.Doc)
+			switch decl.Tok {
+			case token.CONST, token.VAR:
+				tp := "V"
+				if decl.Tok == token.CONST {
+					tp = "C"
+				}
+				if depr {
+					tp += "D"
+				}
+				for _, sp := range decl.Specs {
+					for _, x := range sp.(*ast.ValueSpec).Names {
+						if s := newsym(pkg, x.Name, tp, ""); s != nil {
+							ans = append(ans, *s)
+						}
+					}
+				}
+			case token.TYPE:
+				tp := "T"
+				if depr {
+					tp += "D"
+				}
+				for _, sp := range decl.Specs {
+					if s := newsym(pkg, sp.(*ast.TypeSpec).Name.Name, tp, ""); s != nil {
+						ans = append(ans, *s)
+					}
+				}
+			}
+		}
+	}
+	return ans
+}
+
+func newsym(pkg, name, kind, sig string) *symbol {
+	if len(name) == 0 || !ast.IsExported(name) {
+		return nil
+	}
+	sym := symbol{pkg: pkg, name: name, kind: kind, sig: sig}
+	return &sym
+}
+
+func isDeprecated(doc *ast.CommentGroup) bool {
+	if doc == nil {
+		return false
+	}
+	// go.dev/wiki/Deprecated Paragraph starting 'Deprecated:'
+	// This code fails for /* Deprecated: */, but it's the code from
+	// gopls/internal/analysis/deprecated
+	for line := range strings.SplitSeq(doc.Text(), "\n\n") {
+		if strings.HasPrefix(line, "Deprecated:") {
+			return true
+		}
+	}
+	return false
+}
+
+// return the package name and the value for the symbols.
+// if there are multiple packages, choose one arbitrarily
+// the returned slice is sorted lexicographically
+func processSyms(syms []symbol) (string, []string) {
+	if len(syms) == 0 {
+		return "", nil
+	}
+	slices.SortFunc(syms, func(l, r symbol) int {
+		return strings.Compare(l.name, r.name)
+	})
+	pkg := syms[0].pkg
+	var names []string
+	for _, s := range syms {
+		if s.pkg != pkg {
+			// Symbols came from two files in same dir
+			// with different package declarations.
+			continue
+		}
+		var nx string
+		if s.sig != "" {
+			nx = fmt.Sprintf("%s %s %s", s.name, s.kind, s.sig)
+		} else {
+			nx = fmt.Sprintf("%s %s", s.name, s.kind)
+		}
+		names = append(names, nx)
+	}
+	return pkg, names
+}
diff --git a/vendor/golang.org/x/tools/internal/moremaps/maps.go b/vendor/golang.org/x/tools/internal/moremaps/maps.go
new file mode 100644
index 00000000..a1bae078
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/moremaps/maps.go
@@ -0,0 +1,116 @@
+// Copyright 2023 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 moremaps contains more functions for working with maps.
+package moremaps
+
+import (
+	"cmp"
+	"iter"
+	"maps"
+	"slices"
+)
+
+// Arbitrary returns an arbitrary (key, value) entry from the map and ok is true, if
+// the map is not empty. Otherwise, it returns zero values for K and V, and false.
+func Arbitrary[K comparable, V any](m map[K]V) (_ K, _ V, ok bool) {
+	for k, v := range m {
+		return k, v, true
+	}
+	return
+}
+
+// Group returns a new non-nil map containing the elements of s grouped by the
+// keys returned from the key func.
+func Group[K comparable, V any](s []V, key func(V) K) map[K][]V {
+	m := make(map[K][]V)
+	for _, v := range s {
+		k := key(v)
+		m[k] = append(m[k], v)
+	}
+	return m
+}
+
+// KeySlice returns the keys of the map M, like slices.Collect(maps.Keys(m)).
+func KeySlice[M ~map[K]V, K comparable, V any](m M) []K {
+	r := make([]K, 0, len(m))
+	for k := range m {
+		r = append(r, k)
+	}
+	return r
+}
+
+// ValueSlice returns the values of the map M, like slices.Collect(maps.Values(m)).
+func ValueSlice[M ~map[K]V, K comparable, V any](m M) []V {
+	r := make([]V, 0, len(m))
+	for _, v := range m {
+		r = append(r, v)
+	}
+	return r
+}
+
+// SameKeys reports whether x and y have equal sets of keys.
+func SameKeys[K comparable, V1, V2 any](x map[K]V1, y map[K]V2) bool {
+	ignoreValues := func(V1, V2) bool { return true }
+	return maps.EqualFunc(x, y, ignoreValues)
+}
+
+// Sorted returns an iterator over the entries of m in key order.
+func Sorted[M ~map[K]V, K cmp.Ordered, V any](m M) iter.Seq2[K, V] {
+	// TODO(adonovan): use maps.Sorted if proposal #68598 is accepted.
+	return func(yield func(K, V) bool) {
+		keys := KeySlice(m)
+		slices.Sort(keys)
+		for _, k := range keys {
+			if !yield(k, m[k]) {
+				break
+			}
+		}
+	}
+}
+
+// SortedFunc returns an iterator over the entries of m in the key order determined by cmp.
+func SortedFunc[M ~map[K]V, K comparable, V any](m M, cmp func(x, y K) int) iter.Seq2[K, V] {
+	// TODO(adonovan): use maps.SortedFunc if proposal #68598 is accepted.
+	return func(yield func(K, V) bool) {
+		keys := KeySlice(m)
+		slices.SortFunc(keys, cmp)
+		for _, k := range keys {
+			if !yield(k, m[k]) {
+				break
+			}
+		}
+	}
+}
+
+// Delete is like delete(m, k) but reports whether deletion occurred.
+func Delete[M ~map[K]V, K comparable, V any](m M, k K) bool {
+	pre := len(m)
+	delete(m, k)
+	return pre != len(m)
+}
+
+// Entry is a key-value pair obtained from a map.
+type Entry[K comparable, V any] struct {
+	Key   K
+	Value V
+}
+
+// Entries returns a new unordered array of the entries of a map.
+func Entries[M ~map[K]V, K comparable, V any](m M) []Entry[K, V] {
+	entries := make([]Entry[K, V], 0, len(m))
+	for k, v := range m {
+		entries = append(entries, Entry[K, V]{k, v})
+	}
+	return entries
+}
+
+// FromEntries returns a new map into which the entries have been inserted in order.
+func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V {
+	m := make(map[K]V, len(entries))
+	for _, e := range entries {
+		m[e.Key] = e.Value
+	}
+	return m
+}
diff --git a/vendor/golang.org/x/tools/internal/packagepath/packagepath.go b/vendor/golang.org/x/tools/internal/packagepath/packagepath.go
index fa39a13f..7b25340d 100644
--- a/vendor/golang.org/x/tools/internal/packagepath/packagepath.go
+++ b/vendor/golang.org/x/tools/internal/packagepath/packagepath.go
@@ -36,9 +36,19 @@ func CanImport(from, to string) bool {
 	return true
 }
 
-// IsStdPackage reports whether the specified package path belongs to a
-// package in the standard library (including internal dependencies).
-func IsStdPackage(path string) bool {
+// MaybeStdPackage reports whether the specified package path might
+// belong to a package in the standard library (including internal
+// dependencies), based only on its form.
+//
+// It may spuriously return true, but a result of false is definitive:
+//
+//	MaybeStdPackage("fmt")             = true
+//	MaybeStdPackage("maybe/tomorrow")  = true  // false positive
+//	MaybeStdPackage("example.com/foo") = false
+//
+// For a definitive answer, use [stdlib.HasPackage], which consults a
+// huge table.
+func MaybeStdPackage(path string) bool {
 	// A standard package has no dot in its first segment.
 	// (It may yet have a dot, e.g. "vendor/golang.org/x/foo".)
 	slash := strings.IndexByte(path, '/')
diff --git a/vendor/golang.org/x/tools/internal/refactor/imports.go b/vendor/golang.org/x/tools/internal/refactor/imports.go
index 5ce70aee..046038cc 100644
--- a/vendor/golang.org/x/tools/internal/refactor/imports.go
+++ b/vendor/golang.org/x/tools/internal/refactor/imports.go
@@ -125,13 +125,13 @@ func AddImportEdits(file *ast.File, name, pkgpath string) []Edit {
 	var pos token.Pos
 	if gd, ok := decl0.(*ast.GenDecl); ok && gd.Tok == token.IMPORT && gd.Rparen.IsValid() {
 		// Have existing grouped import ( ... ) decl.
-		if packagepath.IsStdPackage(pkgpath) && len(gd.Specs) > 0 {
+		if packagepath.MaybeStdPackage(pkgpath) && len(gd.Specs) > 0 {
 			// Add spec for a std package before
 			// first existing spec, followed by
 			// a blank line if the next one is non-std.
 			first := gd.Specs[0].(*ast.ImportSpec)
 			pos = first.Pos()
-			if !packagepath.IsStdPackage(first.Path.Value) {
+			if !packagepath.MaybeStdPackage(first.Path.Value) {
 				newText += "\n"
 			}
 			newText += "\n\t"
diff --git a/vendor/golang.org/x/tools/internal/refactor/inline/callee.go b/vendor/golang.org/x/tools/internal/refactor/inline/callee.go
new file mode 100644
index 00000000..313e0f71
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/refactor/inline/callee.go
@@ -0,0 +1,962 @@
+// Copyright 2023 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 inline
+
+// This file defines the analysis of the callee function.
+
+import (
+	"bytes"
+	"cmp"
+	"encoding/gob"
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"slices"
+	"strings"
+
+	"golang.org/x/tools/go/types/typeutil"
+	"golang.org/x/tools/internal/moremaps"
+	"golang.org/x/tools/internal/typeparams"
+	"golang.org/x/tools/internal/typesinternal"
+)
+
+// A Callee holds information about an inlinable function. Gob-serializable.
+type Callee struct {
+	impl gobCallee
+}
+
+func (callee *Callee) String() string { return callee.impl.Name }
+
+type gobCallee struct {
+	Content []byte // file content, compacted to a single func decl
+
+	// results of type analysis (does not reach go/types data structures)
+	PkgPath          string                 // package path of declaring package
+	Name             string                 // user-friendly name for error messages
+	GoVersion        string                 // version of Go effective in callee file
+	Unexported       []string               // names of free objects that are unexported
+	FreeRefs         []freeRef              // locations of references to free objects
+	FreeObjs         []object               // descriptions of free objects
+	ValidForCallStmt bool                   // function body is "return expr" where expr is f() or <-ch
+	NumResults       int                    // number of results (according to type, not ast.FieldList)
+	Params           []*paramInfo           // information about parameters (incl. receiver)
+	TypeParams       []*paramInfo           // information about type parameters
+	Results          []*paramInfo           // information about result variables
+	Effects          []int                  // order in which parameters are evaluated (see calleefx)
+	HasDefer         bool                   // uses defer
+	HasBareReturn    bool                   // uses bare return in non-void function
+	Returns          [][]returnOperandFlags // metadata about result expressions for each return
+	Labels           []string               // names of all control labels
+	Falcon           falconResult           // falcon constraint system
+}
+
+// returnOperandFlags records metadata about a single result expression in a return
+// statement.
+type returnOperandFlags int
+
+const (
+	nonTrivialResult returnOperandFlags = 1 << iota // return operand has non-trivial conversion to result type
+	untypedNilResult                                // return operand is nil literal
+)
+
+// A freeRef records a reference to a free object. Gob-serializable.
+// (This means free relative to the FuncDecl as a whole, i.e. excluding parameters.)
+type freeRef struct {
+	Offset int // byte offset of the reference relative to the FuncDecl
+	Object int // index into Callee.freeObjs
+}
+
+// An object abstracts a free types.Object referenced by the callee. Gob-serializable.
+type object struct {
+	Name    string // Object.Name()
+	Kind    string // one of {var,func,const,type,pkgname,nil,builtin}
+	PkgPath string // path of object's package (or imported package if kind="pkgname")
+	PkgName string // name of object's package (or imported package if kind="pkgname")
+	// TODO(rfindley): should we also track LocalPkgName here? Do we want to
+	// preserve the local package name?
+	ValidPos bool      // Object.Pos().IsValid()
+	Shadow   shadowMap // shadowing info for the object's refs
+}
+
+// AnalyzeCallee analyzes a function that is a candidate for inlining
+// and returns a Callee that describes it. The Callee object, which is
+// serializable, can be passed to one or more subsequent calls to
+// Inline, each with a different Caller.
+//
+// This design allows separate analysis of callers and callees in the
+// golang.org/x/tools/go/analysis framework: the inlining information
+// about a callee can be recorded as a "fact".
+//
+// The content should be the actual input to the compiler, not the
+// apparent source file according to any //line directives that
+// may be present within it.
+func AnalyzeCallee(logf func(string, ...any), fset *token.FileSet, pkg *types.Package, info *types.Info, decl *ast.FuncDecl, content []byte) (*Callee, error) {
+	checkInfoFields(info)
+
+	// The client is expected to have determined that the callee
+	// is a function with a declaration (not a built-in or var).
+	fn := info.Defs[decl.Name].(*types.Func)
+	sig := fn.Type().(*types.Signature)
+
+	logf("analyzeCallee %v @ %v", fn, fset.PositionFor(decl.Pos(), false))
+
+	// Create user-friendly name ("pkg.Func" or "(pkg.T).Method")
+	var name string
+	if sig.Recv() == nil {
+		name = fmt.Sprintf("%s.%s", fn.Pkg().Name(), fn.Name())
+	} else {
+		name = fmt.Sprintf("(%s).%s", types.TypeString(sig.Recv().Type(), (*types.Package).Name), fn.Name())
+	}
+
+	if decl.Body == nil {
+		return nil, fmt.Errorf("cannot inline function %s as it has no body", name)
+	}
+
+	// Record the file's Go goVersion so that we don't
+	// inline newer code into file using an older dialect.
+	//
+	// Using the file version is overly conservative.
+	// A more precise solution would be for the type checker to
+	// record which language features the callee actually needs;
+	// see https://go.dev/issue/75726.
+	//
+	// We don't have the ast.File handy, so instead of a
+	// lookup we must scan the entire FileVersions map.
+	var goVersion string
+	for file, v := range info.FileVersions {
+		if file.Pos() < decl.Pos() && decl.Pos() < file.End() {
+			goVersion = v
+			break
+		}
+	}
+
+	// Record the location of all free references in the FuncDecl.
+	// (Parameters are not free by this definition.)
+	var (
+		fieldObjs    = fieldObjs(sig)
+		freeObjIndex = make(map[types.Object]int)
+		freeObjs     []object
+		freeRefs     []freeRef // free refs that may need renaming
+		unexported   []string  // free refs to unexported objects, for later error checks
+	)
+	var f func(n ast.Node, stack []ast.Node) bool
+	var stack []ast.Node
+	stack = append(stack, decl.Type) // for scope of function itself
+	visit := func(n ast.Node, stack []ast.Node) { ast.PreorderStack(n, stack, f) }
+	f = func(n ast.Node, stack []ast.Node) bool {
+		switch n := n.(type) {
+		case *ast.SelectorExpr:
+			// Check selections of free fields/methods.
+			if sel, ok := info.Selections[n]; ok &&
+				!within(sel.Obj().Pos(), decl) &&
+				!n.Sel.IsExported() {
+				sym := fmt.Sprintf("(%s).%s", info.TypeOf(n.X), n.Sel.Name)
+				unexported = append(unexported, sym)
+			}
+
+			// Don't recur into SelectorExpr.Sel.
+			visit(n.X, stack)
+			return false
+
+		case *ast.CompositeLit:
+			// Check for struct literals that refer to unexported fields,
+			// whether keyed or unkeyed. (Logic assumes well-typedness.)
+			litType := typeparams.Deref(info.TypeOf(n))
+			if s, ok := typeparams.CoreType(litType).(*types.Struct); ok {
+				if n.Type != nil {
+					visit(n.Type, stack)
+				}
+				for i, elt := range n.Elts {
+					var field *types.Var
+					var value ast.Expr
+					if kv, ok := elt.(*ast.KeyValueExpr); ok {
+						field = info.Uses[kv.Key.(*ast.Ident)].(*types.Var)
+						value = kv.Value
+					} else {
+						field = s.Field(i)
+						value = elt
+					}
+					if !within(field.Pos(), decl) && !field.Exported() {
+						sym := fmt.Sprintf("(%s).%s", litType, field.Name())
+						unexported = append(unexported, sym)
+					}
+
+					// Don't recur into KeyValueExpr.Key.
+					visit(value, stack)
+				}
+				return false
+			}
+
+		case *ast.Ident:
+			if obj, ok := info.Uses[n]; ok {
+				// Methods and fields are handled by SelectorExpr and CompositeLit.
+				if isField(obj) || isMethod(obj) {
+					panic(obj)
+				}
+				// Inv: id is a lexical reference.
+
+				// A reference to an unexported package-level declaration
+				// cannot be inlined into another package.
+				if !n.IsExported() &&
+					obj.Pkg() != nil && obj.Parent() == obj.Pkg().Scope() {
+					unexported = append(unexported, n.Name)
+				}
+
+				// Record free reference (incl. self-reference).
+				if obj == fn || !within(obj.Pos(), decl) {
+					objidx, ok := freeObjIndex[obj]
+					if !ok {
+						objidx = len(freeObjIndex)
+						var pkgPath, pkgName string
+						if pn, ok := obj.(*types.PkgName); ok {
+							pkgPath = pn.Imported().Path()
+							pkgName = pn.Imported().Name()
+						} else if obj.Pkg() != nil {
+							pkgPath = obj.Pkg().Path()
+							pkgName = obj.Pkg().Name()
+						}
+						freeObjs = append(freeObjs, object{
+							Name:     obj.Name(),
+							Kind:     objectKind(obj),
+							PkgName:  pkgName,
+							PkgPath:  pkgPath,
+							ValidPos: obj.Pos().IsValid(),
+						})
+						freeObjIndex[obj] = objidx
+					}
+
+					freeObjs[objidx].Shadow = freeObjs[objidx].Shadow.add(info, fieldObjs, obj.Name(), stack)
+
+					freeRefs = append(freeRefs, freeRef{
+						Offset: int(n.Pos() - decl.Pos()),
+						Object: objidx,
+					})
+				}
+			}
+		}
+		return true
+	}
+	visit(decl, stack)
+
+	// Analyze callee body for "return expr" form,
+	// where expr is f() or <-ch. These forms are
+	// safe to inline as a standalone statement.
+	validForCallStmt := false
+	if len(decl.Body.List) != 1 {
+		// not just a return statement
+	} else if ret, ok := decl.Body.List[0].(*ast.ReturnStmt); ok && len(ret.Results) == 1 {
+		validForCallStmt = func() bool {
+			switch expr := ast.Unparen(ret.Results[0]).(type) {
+			case *ast.CallExpr: // f(x)
+				callee := typeutil.Callee(info, expr)
+				if callee == nil {
+					return false // conversion T(x)
+				}
+
+				// The only non-void built-in functions that may be
+				// called as a statement are copy and recover
+				// (though arguably a call to recover should never
+				// be inlined as that changes its behavior).
+				if builtin, ok := callee.(*types.Builtin); ok {
+					return builtin.Name() == "copy" ||
+						builtin.Name() == "recover"
+				}
+
+				return true // ordinary call f()
+
+			case *ast.UnaryExpr: // <-x
+				return expr.Op == token.ARROW // channel receive <-ch
+			}
+
+			// No other expressions are valid statements.
+			return false
+		}()
+	}
+
+	// Record information about control flow in the callee
+	// (but not any nested functions).
+	var (
+		hasDefer      = false
+		hasBareReturn = false
+		returnInfo    [][]returnOperandFlags
+		labels        []string
+	)
+	ast.Inspect(decl.Body, func(n ast.Node) bool {
+		switch n := n.(type) {
+		case *ast.FuncLit:
+			return false // prune traversal
+		case *ast.DeferStmt:
+			hasDefer = true
+		case *ast.LabeledStmt:
+			labels = append(labels, n.Label.Name)
+		case *ast.ReturnStmt:
+
+			// Are implicit assignment conversions
+			// to result variables all trivial?
+			var resultInfo []returnOperandFlags
+			if len(n.Results) > 0 {
+				argInfo := func(i int) (ast.Expr, types.Type) {
+					expr := n.Results[i]
+					return expr, info.TypeOf(expr)
+				}
+				if len(n.Results) == 1 && sig.Results().Len() > 1 {
+					// Spread return: return f() where f.Results > 1.
+					tuple := info.TypeOf(n.Results[0]).(*types.Tuple)
+					argInfo = func(i int) (ast.Expr, types.Type) {
+						return nil, tuple.At(i).Type()
+					}
+				}
+				for i := range sig.Results().Len() {
+					expr, typ := argInfo(i)
+					var flags returnOperandFlags
+					if typ == types.Typ[types.UntypedNil] { // untyped nil is preserved by go/types
+						flags |= untypedNilResult
+					}
+					if !trivialConversion(info.Types[expr].Value, typ, sig.Results().At(i).Type()) {
+						flags |= nonTrivialResult
+					}
+					resultInfo = append(resultInfo, flags)
+				}
+			} else if sig.Results().Len() > 0 {
+				hasBareReturn = true
+			}
+			returnInfo = append(returnInfo, resultInfo)
+		}
+		return true
+	})
+
+	// Reject attempts to inline cgo-generated functions.
+	for _, obj := range freeObjs {
+		// There are others (iconst fconst sconst fpvar macro)
+		// but this is probably sufficient.
+		if strings.HasPrefix(obj.Name, "_Cfunc_") ||
+			strings.HasPrefix(obj.Name, "_Ctype_") ||
+			strings.HasPrefix(obj.Name, "_Cvar_") {
+			return nil, fmt.Errorf("cannot inline cgo-generated functions")
+		}
+	}
+
+	// Compact content to just the FuncDecl.
+	//
+	// As a space optimization, we don't retain the complete
+	// callee file content; all we need is "package _; func f() { ... }".
+	// This reduces the size of analysis facts.
+	//
+	// Offsets in the callee information are "relocatable"
+	// since they are all relative to the FuncDecl.
+
+	content = append([]byte("package _\n"),
+		content[offsetOf(fset, decl.Pos()):offsetOf(fset, decl.End())]...)
+	// Sanity check: re-parse the compacted content.
+	if _, _, err := parseCompact(content); err != nil {
+		return nil, err
+	}
+
+	params, results, effects, falcon := analyzeParams(logf, fset, info, decl)
+	tparams := analyzeTypeParams(logf, fset, info, decl)
+	return &Callee{gobCallee{
+		Content:          content,
+		PkgPath:          pkg.Path(),
+		Name:             name,
+		GoVersion:        goVersion,
+		Unexported:       unexported,
+		FreeObjs:         freeObjs,
+		FreeRefs:         freeRefs,
+		ValidForCallStmt: validForCallStmt,
+		NumResults:       sig.Results().Len(),
+		Params:           params,
+		TypeParams:       tparams,
+		Results:          results,
+		Effects:          effects,
+		HasDefer:         hasDefer,
+		HasBareReturn:    hasBareReturn,
+		Returns:          returnInfo,
+		Labels:           labels,
+		Falcon:           falcon,
+	}}, nil
+}
+
+// parseCompact parses a Go source file of the form "package _\n func f() { ... }"
+// and returns the sole function declaration.
+func parseCompact(content []byte) (*token.FileSet, *ast.FuncDecl, error) {
+	fset := token.NewFileSet()
+	const mode = parser.ParseComments | parser.SkipObjectResolution | parser.AllErrors
+	f, err := parser.ParseFile(fset, "callee.go", content, mode)
+	if err != nil {
+		return nil, nil, fmt.Errorf("internal error: cannot compact file: %v", err)
+	}
+	return fset, f.Decls[0].(*ast.FuncDecl), nil
+}
+
+// A paramInfo records information about a callee receiver, parameter, or result variable.
+type paramInfo struct {
+	Name        string    // parameter name (may be blank, or even "")
+	Index       int       // index within signature
+	IsResult    bool      // false for receiver or parameter, true for result variable
+	IsInterface bool      // parameter has a (non-type parameter) interface type
+	Assigned    bool      // parameter appears on left side of an assignment statement
+	Escapes     bool      // parameter has its address taken
+	Refs        []refInfo // information about references to parameter within body
+	Shadow      shadowMap // shadowing info for the above refs; see [shadowMap]
+	FalconType  string    // name of this parameter's type (if basic) in the falcon system
+}
+
+type refInfo struct {
+	Offset           int  // FuncDecl-relative byte offset of parameter ref within body
+	Assignable       bool // ref appears in context of assignment to known type
+	IfaceAssignment  bool // ref is being assigned to an interface
+	AffectsInference bool // ref type may affect type inference
+	// IsSelectionOperand indicates whether the parameter reference is the
+	// operand of a selection (param.f). If so, and param's argument is itself
+	// a receiver parameter (a common case), we don't need to desugar (&v or *ptr)
+	// the selection: if param.Method is a valid selection, then so is param.fieldOrMethod.
+	IsSelectionOperand bool
+}
+
+// analyzeParams computes information about parameters of the function declared by decl,
+// including a simple "address taken" escape analysis.
+//
+// It returns two new arrays, one of the receiver and parameters, and
+// the other of the result variables of the function.
+//
+// The input must be well-typed.
+func analyzeParams(logf func(string, ...any), fset *token.FileSet, info *types.Info, decl *ast.FuncDecl) (params, results []*paramInfo, effects []int, _ falconResult) {
+	sig := signature(fset, info, decl)
+
+	paramInfos := make(map[*types.Var]*paramInfo)
+	{
+		newParamInfo := func(param *types.Var, isResult bool) *paramInfo {
+			info := ¶mInfo{
+				Name:        param.Name(),
+				IsResult:    isResult,
+				Index:       len(paramInfos),
+				IsInterface: isNonTypeParamInterface(param.Type()),
+			}
+			paramInfos[param] = info
+			return info
+		}
+		if sig.Recv() != nil {
+			params = append(params, newParamInfo(sig.Recv(), false))
+		}
+		for v := range sig.Params().Variables() {
+			params = append(params, newParamInfo(v, false))
+		}
+		for v := range sig.Results().Variables() {
+			results = append(results, newParamInfo(v, true))
+		}
+	}
+
+	// Search function body for operations &x, x.f(), and x = y
+	// where x is a parameter, and record it.
+	escape(info, decl, func(v *types.Var, escapes bool) {
+		if info := paramInfos[v]; info != nil {
+			if escapes {
+				info.Escapes = true
+			} else {
+				info.Assigned = true
+			}
+		}
+	})
+
+	// Record locations of all references to parameters.
+	// And record the set of intervening definitions for each parameter.
+	//
+	// TODO(adonovan): combine this traversal with the one that computes
+	// FreeRefs. The tricky part is that calleefx needs this one first.
+	fieldObjs := fieldObjs(sig)
+	var stack []ast.Node
+	stack = append(stack, decl.Type) // for scope of function itself
+	ast.PreorderStack(decl.Body, stack, func(n ast.Node, stack []ast.Node) bool {
+		if id, ok := n.(*ast.Ident); ok {
+			if v, ok := info.Uses[id].(*types.Var); ok {
+				if pinfo, ok := paramInfos[v]; ok {
+					// Record ref information, and any intervening (shadowing) names.
+					//
+					// If the parameter v has an interface type, and the reference id
+					// appears in a context where assignability rules apply, there may be
+					// an implicit interface-to-interface widening. In that case it is
+					// not necessary to insert an explicit conversion from the argument
+					// to the parameter's type.
+					//
+					// Contrapositively, if param is not an interface type, then the
+					// assignment may lose type information, for example in the case that
+					// the substituted expression is an untyped constant or unnamed type.
+					stack = append(stack, n) // (the two calls below want n)
+					assignable, ifaceAssign, affectsInference := analyzeAssignment(info, stack)
+					ref := refInfo{
+						Offset:             int(n.Pos() - decl.Pos()),
+						Assignable:         assignable,
+						IfaceAssignment:    ifaceAssign,
+						AffectsInference:   affectsInference,
+						IsSelectionOperand: isSelectionOperand(stack),
+					}
+					pinfo.Refs = append(pinfo.Refs, ref)
+					pinfo.Shadow = pinfo.Shadow.add(info, fieldObjs, pinfo.Name, stack)
+				}
+			}
+		}
+		return true
+	})
+
+	// Compute subset and order of parameters that are strictly evaluated.
+	// (Depends on Refs computed above.)
+	effects = calleefx(info, decl.Body, paramInfos)
+	logf("effects list = %v", effects)
+
+	falcon := falcon(logf, fset, paramInfos, info, decl)
+
+	return params, results, effects, falcon
+}
+
+// analyzeTypeParams computes information about the type parameters of the function declared by decl.
+func analyzeTypeParams(_ logger, fset *token.FileSet, info *types.Info, decl *ast.FuncDecl) []*paramInfo {
+	sig := signature(fset, info, decl)
+	paramInfos := make(map[*types.TypeName]*paramInfo)
+	var params []*paramInfo
+	collect := func(tpl *types.TypeParamList) {
+		for tparam := range tpl.TypeParams() {
+			typeName := tparam.Obj()
+			info := ¶mInfo{Name: typeName.Name()}
+			params = append(params, info)
+			paramInfos[typeName] = info
+		}
+	}
+	collect(sig.RecvTypeParams())
+	collect(sig.TypeParams())
+
+	// Find references.
+	// We don't care about most of the properties that matter for parameter references:
+	// a type is immutable, cannot have its address taken, and does not undergo conversions.
+	// TODO(jba): can we nevertheless combine this with the traversal in analyzeParams?
+	visit := func(n ast.Node, stack []ast.Node) bool {
+		if id, ok := n.(*ast.Ident); ok {
+			if v, ok := info.Uses[id].(*types.TypeName); ok {
+				if pinfo, ok := paramInfos[v]; ok {
+					ref := refInfo{Offset: int(n.Pos() - decl.Pos())}
+					pinfo.Refs = append(pinfo.Refs, ref)
+					pinfo.Shadow = pinfo.Shadow.add(info, nil, pinfo.Name, stack)
+				}
+			}
+		}
+		return true
+	}
+	var stack []ast.Node
+	stack = append(stack, decl.Type) // for scope of function itself
+	if decl.Type.Params != nil {
+		ast.PreorderStack(decl.Type.Params, stack, visit)
+	}
+	if decl.Type.Results != nil {
+		ast.PreorderStack(decl.Type.Results, stack, visit)
+	}
+	ast.PreorderStack(decl.Body, stack, visit)
+	return params
+}
+
+func signature(fset *token.FileSet, info *types.Info, decl *ast.FuncDecl) *types.Signature {
+	fnobj, ok := info.Defs[decl.Name]
+	if !ok {
+		panic(fmt.Sprintf("%s: no func object for %q",
+			fset.PositionFor(decl.Name.Pos(), false), decl.Name)) // ill-typed?
+	}
+	return fnobj.Type().(*types.Signature)
+}
+
+// -- callee helpers --
+
+// analyzeAssignment looks at the given stack, and analyzes certain
+// attributes of the innermost expression.
+//
+// In all cases we 'fail closed' when we cannot detect (or for simplicity
+// choose not to detect) the condition in question, meaning we err on the side
+// of the more restrictive rule. This is noted for each result below.
+//
+//   - assignable reports whether the expression is used in a position where
+//     assignability rules apply, such as in an actual assignment, as call
+//     argument, or in a send to a channel. Defaults to 'false'. If assignable
+//     is false, the other two results are irrelevant.
+//   - ifaceAssign reports whether that assignment is to an interface type.
+//     This is important as we want to preserve the concrete type in that
+//     assignment. Defaults to 'true'. Notably, if the assigned type is a type
+//     parameter, we assume that it could have interface type.
+//   - affectsInference is (somewhat vaguely) defined as whether or not the
+//     type of the operand may affect the type of the surrounding syntax,
+//     through type inference. It is infeasible to completely reverse engineer
+//     type inference, so we over approximate: if the expression is an argument
+//     to a call to a generic function (but not method!) that uses type
+//     parameters, assume that unification of that argument may affect the
+//     inferred types.
+func analyzeAssignment(info *types.Info, stack []ast.Node) (assignable, ifaceAssign, affectsInference bool) {
+	remaining, parent, expr := exprContext(stack)
+	if parent == nil {
+		return false, false, false
+	}
+
+	// TODO(golang/go#70638): simplify when types.Info records implicit conversions.
+
+	// Types do not need to match for assignment to a variable.
+	if assign, ok := parent.(*ast.AssignStmt); ok {
+		for i, v := range assign.Rhs {
+			if v == expr {
+				if i >= len(assign.Lhs) {
+					return false, false, false // ill typed
+				}
+				// Check to see if the assignment is to an interface type.
+				if i < len(assign.Lhs) {
+					// TODO: We could handle spread calls here, but in current usage expr
+					// is an ident.
+					if id, _ := assign.Lhs[i].(*ast.Ident); id != nil && info.Defs[id] != nil {
+						// Types must match for a defining identifier in a short variable
+						// declaration.
+						return false, false, false
+					}
+					// In all other cases, types should be known.
+					typ := info.TypeOf(assign.Lhs[i])
+					return true, typ == nil || types.IsInterface(typ), false
+				}
+				// Default:
+				return assign.Tok == token.ASSIGN, true, false
+			}
+		}
+	}
+
+	// Types do not need to match for an initializer with known type.
+	if spec, ok := parent.(*ast.ValueSpec); ok && spec.Type != nil {
+		if slices.Contains(spec.Values, expr) {
+			typ := info.TypeOf(spec.Type)
+			return true, typ == nil || types.IsInterface(typ), false
+		}
+	}
+
+	// Types do not need to match for index expressions.
+	if ix, ok := parent.(*ast.IndexExpr); ok {
+		if ix.Index == expr {
+			typ := info.TypeOf(ix.X)
+			if typ == nil {
+				return true, true, false
+			}
+			m, _ := typeparams.CoreType(typ).(*types.Map)
+			return true, m == nil || types.IsInterface(m.Key()), false
+		}
+	}
+
+	// Types do not need to match for composite literal keys, values, or
+	// fields.
+	if kv, ok := parent.(*ast.KeyValueExpr); ok {
+		var under types.Type
+		if len(remaining) > 0 {
+			if complit, ok := remaining[len(remaining)-1].(*ast.CompositeLit); ok {
+				if typ := info.TypeOf(complit); typ != nil {
+					// Unpointer to allow for pointers to slices or arrays, which are
+					// permitted as the types of nested composite literals without a type
+					// name.
+					under = typesinternal.Unpointer(typeparams.CoreType(typ))
+				}
+			}
+		}
+		if kv.Key == expr { // M{expr: ...}: assign to map key
+			m, _ := under.(*types.Map)
+			return true, m == nil || types.IsInterface(m.Key()), false
+		}
+		if kv.Value == expr {
+			switch under := under.(type) {
+			case interface{ Elem() types.Type }: // T{...: expr}: assign to map/array/slice element
+				return true, types.IsInterface(under.Elem()), false
+			case *types.Struct: // Struct{k: expr}
+				if id, _ := kv.Key.(*ast.Ident); id != nil {
+					for field := range under.Fields() {
+						if info.Uses[id] == field {
+							return true, types.IsInterface(field.Type()), false
+						}
+					}
+				}
+			default:
+				return true, true, false
+			}
+		}
+	}
+	if lit, ok := parent.(*ast.CompositeLit); ok {
+		for i, v := range lit.Elts {
+			if v == expr {
+				typ := info.TypeOf(lit)
+				if typ == nil {
+					return true, true, false
+				}
+				// As in the KeyValueExpr case above, unpointer to handle pointers to
+				// array/slice literals.
+				under := typesinternal.Unpointer(typeparams.CoreType(typ))
+				switch under := under.(type) {
+				case interface{ Elem() types.Type }: // T{expr}: assign to map/array/slice element
+					return true, types.IsInterface(under.Elem()), false
+				case *types.Struct: // Struct{expr}: assign to unkeyed struct field
+					if i < under.NumFields() {
+						return true, types.IsInterface(under.Field(i).Type()), false
+					}
+				}
+				return true, true, false
+			}
+		}
+	}
+
+	// Types do not need to match for values sent to a channel.
+	if send, ok := parent.(*ast.SendStmt); ok {
+		if send.Value == expr {
+			typ := info.TypeOf(send.Chan)
+			if typ == nil {
+				return true, true, false
+			}
+			ch, _ := typeparams.CoreType(typ).(*types.Chan)
+			return true, ch == nil || types.IsInterface(ch.Elem()), false
+		}
+	}
+
+	// Types do not need to match for an argument to a call, unless the
+	// corresponding parameter has type parameters, as in that case the
+	// argument type may affect inference.
+	if call, ok := parent.(*ast.CallExpr); ok {
+		if _, ok := isConversion(info, call); ok {
+			return false, false, false // redundant conversions are handled at the call site
+		}
+		// Ordinary call. Could be a call of a func, builtin, or function value.
+		for i, arg := range call.Args {
+			if arg == expr {
+				typ := info.TypeOf(call.Fun)
+				if typ == nil {
+					return true, true, false
+				}
+				sig, _ := typeparams.CoreType(typ).(*types.Signature)
+				if sig != nil {
+					// Find the relevant parameter type, accounting for variadics.
+					paramType := paramTypeAtIndex(sig, call, i)
+					ifaceAssign := paramType == nil || types.IsInterface(paramType)
+					affectsInference := false
+					switch callee := typeutil.Callee(info, call).(type) {
+					case *types.Builtin:
+						// Consider this litmus test:
+						//
+						//   func f(x int64) any { return max(x) }
+						//   func main() { fmt.Printf("%T", f(42)) }
+						//
+						// If we lose the implicit conversion from untyped int
+						// to int64, the type inferred for the max(x) call changes,
+						// resulting in a different dynamic behavior: it prints
+						// int, not int64.
+						//
+						// Inferred result type affected:
+						//    new
+						//    complex, real, imag
+						//    min, max
+						//
+						// Dynamic behavior change:
+						//    append         -- dynamic type of append([]any(nil), x)[0]
+						//    delete(m, x)   -- dynamic key type where m is map[any]unit
+						//    panic          -- dynamic type of panic value
+						//
+						// Unaffected:
+						//    recover
+						//    make
+						//    len, cap
+						//    clear
+						//    close
+						//    copy
+						//    print, println  -- only uses underlying types (?)
+						//
+						// The dynamic type cases are all covered by
+						// the ifaceAssign logic.
+						switch callee.Name() {
+						case "new", "complex", "real", "imag", "min", "max":
+							affectsInference = true
+						}
+
+					case *types.Func:
+						// Only standalone (non-method) functions have type
+						// parameters affected by the call arguments.
+						if sig2 := callee.Signature(); sig2.Recv() == nil {
+							originParamType := paramTypeAtIndex(sig2, call, i)
+							affectsInference = originParamType == nil || new(typeparams.Free).Has(originParamType)
+						}
+					}
+					return true, ifaceAssign, affectsInference
+				}
+			}
+		}
+	}
+
+	return false, false, false
+}
+
+// paramTypeAtIndex returns the effective parameter type at the given argument
+// index in call, if valid.
+func paramTypeAtIndex(sig *types.Signature, call *ast.CallExpr, index int) types.Type {
+	if plen := sig.Params().Len(); sig.Variadic() && index >= plen-1 && !call.Ellipsis.IsValid() {
+		if s, ok := sig.Params().At(plen - 1).Type().(*types.Slice); ok {
+			return s.Elem()
+		}
+	} else if index < plen {
+		return sig.Params().At(index).Type()
+	}
+	return nil // ill typed
+}
+
+// exprContext returns the innermost parent->child expression nodes for the
+// given outer-to-inner stack, after stripping parentheses, along with the
+// remaining stack up to the parent node.
+//
+// If no such context exists, returns (nil, nil, nil).
+func exprContext(stack []ast.Node) (remaining []ast.Node, parent ast.Node, expr ast.Expr) {
+	expr, _ = stack[len(stack)-1].(ast.Expr)
+	if expr == nil {
+		return nil, nil, nil
+	}
+	i := len(stack) - 2
+	for ; i >= 0; i-- {
+		if pexpr, ok := stack[i].(*ast.ParenExpr); ok {
+			expr = pexpr
+		} else {
+			parent = stack[i]
+			break
+		}
+	}
+	if parent == nil {
+		return nil, nil, nil
+	}
+	// inv: i is the index of parent in the stack.
+	return stack[:i], parent, expr
+}
+
+// isSelectionOperand reports whether the innermost node of stack is operand
+// (x) of a selection x.f.
+func isSelectionOperand(stack []ast.Node) bool {
+	_, parent, expr := exprContext(stack)
+	if parent == nil {
+		return false
+	}
+	sel, ok := parent.(*ast.SelectorExpr)
+	return ok && sel.X == expr
+}
+
+// A shadowMap records information about shadowing at any of the parameter's
+// references within the callee decl.
+//
+// For each name shadowed at a reference to the parameter within the callee
+// body, shadow map records the 1-based index of the callee decl parameter
+// causing the shadowing, or -1, if the shadowing is not due to a callee decl.
+// A value of zero (or missing) indicates no shadowing. By convention,
+// self-shadowing is excluded from the map.
+//
+// For example, in the following callee
+//
+//	func f(a, b int) int {
+//		c := 2 + b
+//		return a + c
+//	}
+//
+// the shadow map of a is {b: 2, c: -1}, because b is shadowed by the 2nd
+// parameter. The shadow map of b is {a: 1}, because c is not shadowed at the
+// use of b.
+type shadowMap map[string]int
+
+// add returns the [shadowMap] augmented by the set of names
+// locally shadowed at the location of the reference in the callee
+// (identified by the stack). The name of the reference itself is
+// excluded.
+//
+// These shadowed names may not be used in a replacement expression
+// for the reference.
+func (s shadowMap) add(info *types.Info, paramIndexes map[types.Object]int, exclude string, stack []ast.Node) shadowMap {
+	for _, n := range stack {
+		if scope := scopeFor(info, n); scope != nil {
+			for _, name := range scope.Names() {
+				if name != exclude {
+					if s == nil {
+						s = make(shadowMap)
+					}
+					obj := scope.Lookup(name)
+					if idx, ok := paramIndexes[obj]; ok {
+						s[name] = idx + 1
+					} else {
+						s[name] = -1
+					}
+				}
+			}
+		}
+	}
+	return s
+}
+
+var (
+	_ gob.GobEncoder = (*shadowMap)(nil)
+	_ gob.GobDecoder = (*shadowMap)(nil)
+)
+
+// GobEncode implements gob.GobEncoder, encoding the map's entries in a
+// deterministic order so that serialized facts are stable.
+func (s *shadowMap) GobEncode() ([]byte, error) {
+	entries := moremaps.Entries(*s)
+	slices.SortFunc(entries, func(x, y moremaps.Entry[string, int]) int {
+		return cmp.Compare(x.Key, y.Key)
+	})
+	var out bytes.Buffer
+	if err := gob.NewEncoder(&out).Encode(entries); err != nil {
+		return nil, err
+	}
+	return out.Bytes(), nil
+}
+
+func (s *shadowMap) GobDecode(data []byte) error {
+	var entries []moremaps.Entry[string, int]
+	if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&entries); err != nil {
+		return err
+	}
+	*s = moremaps.FromEntries(entries)
+	return nil
+}
+
+// fieldObjs returns a map of each types.Object defined by the given signature
+// to its index in the parameter list. Parameters with missing or blank name
+// are skipped.
+func fieldObjs(sig *types.Signature) map[types.Object]int {
+	m := make(map[types.Object]int)
+	for i := range sig.Params().Len() {
+		if p := sig.Params().At(i); p.Name() != "" && p.Name() != "_" {
+			m[p] = i
+		}
+	}
+	return m
+}
+
+func isField(obj types.Object) bool {
+	if v, ok := obj.(*types.Var); ok && v.IsField() {
+		return true
+	}
+	return false
+}
+
+func isMethod(obj types.Object) bool {
+	if f, ok := obj.(*types.Func); ok && f.Type().(*types.Signature).Recv() != nil {
+		return true
+	}
+	return false
+}
+
+// -- serialization --
+
+var (
+	_ gob.GobEncoder = (*Callee)(nil)
+	_ gob.GobDecoder = (*Callee)(nil)
+)
+
+func (callee *Callee) GobEncode() ([]byte, error) {
+	var out bytes.Buffer
+	if err := gob.NewEncoder(&out).Encode(callee.impl); err != nil {
+		return nil, err
+	}
+	return out.Bytes(), nil
+}
+
+func (callee *Callee) GobDecode(data []byte) error {
+	return gob.NewDecoder(bytes.NewReader(data)).Decode(&callee.impl)
+}
diff --git a/vendor/golang.org/x/tools/internal/refactor/inline/calleefx.go b/vendor/golang.org/x/tools/internal/refactor/inline/calleefx.go
new file mode 100644
index 00000000..6dcf0b97
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/refactor/inline/calleefx.go
@@ -0,0 +1,356 @@
+// Copyright 2023 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 inline
+
+// This file defines the analysis of callee effects.
+
+import (
+	"go/ast"
+	"go/token"
+	"go/types"
+	"slices"
+
+	"golang.org/x/tools/internal/typesinternal"
+)
+
+const (
+	rinf = -1 //  R∞: arbitrary read from memory
+	winf = -2 //  W∞: arbitrary write to memory (or unknown control)
+)
+
+// calleefx returns a list of parameter indices indicating the order
+// in which parameters are first referenced during evaluation of the
+// callee, relative both to each other and to other effects of the
+// callee (if any), such as arbitrary reads (rinf) and arbitrary
+// effects (winf), including unknown control flow. Each parameter
+// that is referenced appears once in the list.
+//
+// For example, the effects list of this function:
+//
+//	func f(x, y, z int) int {
+//	    return y + x + g() + z
+//	}
+//
+// is [1 0 -2 2], indicating reads of y and x, followed by the unknown
+// effects of the g() call, and finally the read of parameter z. This
+// information is used during inlining to ascertain when it is safe
+// for parameter references to be replaced by their corresponding
+// argument expressions. Such substitutions are permitted only when
+// they do not cause "write" operations (those with effects) to
+// commute with "read" operations (those that have no effect but are
+// not pure). Impure operations may be reordered with other impure
+// operations, and pure operations may be reordered arbitrarily.
+//
+// The analysis ignores the effects of runtime panics, on the
+// assumption that well-behaved programs shouldn't encounter them.
+func calleefx(info *types.Info, body *ast.BlockStmt, paramInfos map[*types.Var]*paramInfo) []int {
+	// This traversal analyzes the callee's statements (in syntax
+	// form, though one could do better with SSA) to compute the
+	// sequence of events of the following kinds:
+	//
+	// 1  read of a parameter variable.
+	// 2. reads from other memory.
+	// 3. writes to memory
+
+	var effects []int // indices of parameters, or rinf/winf (-ve)
+	seen := make(map[int]bool)
+	effect := func(i int) {
+		if !seen[i] {
+			seen[i] = true
+			effects = append(effects, i)
+		}
+	}
+
+	// unknown is called for statements of unknown effects (or control).
+	unknown := func() {
+		effect(winf)
+
+		// Ensure that all remaining parameters are "seen"
+		// after we go into the unknown (unless they are
+		// unreferenced by the function body). This lets us
+		// not bother implementing the complete traversal into
+		// control structures.
+
+		// Sort params by Index for determinism
+		sortedParams := make([]*types.Var, 0, len(paramInfos))
+		for obj, pinfo := range paramInfos {
+			if !pinfo.IsResult && len(pinfo.Refs) > 0 {
+				sortedParams = append(sortedParams, obj)
+			}
+		}
+		slices.SortFunc(sortedParams, func(a, b *types.Var) int {
+			return paramInfos[a].Index - paramInfos[b].Index
+		})
+		for _, obj := range sortedParams {
+			effect(paramInfos[obj].Index)
+		}
+	}
+
+	var visitExpr func(n ast.Expr)
+	var visitStmt func(n ast.Stmt) bool
+	visitExpr = func(n ast.Expr) {
+		switch n := n.(type) {
+		case *ast.Ident:
+			if v, ok := info.Uses[n].(*types.Var); ok && !v.IsField() {
+				// Use of global?
+				if v.Parent() == v.Pkg().Scope() {
+					effect(rinf) // read global var
+				}
+
+				// Use of parameter?
+				if pinfo, ok := paramInfos[v]; ok && !pinfo.IsResult {
+					effect(pinfo.Index) // read parameter var
+				}
+
+				// Use of local variables is ok.
+			}
+
+		case *ast.BasicLit:
+			// no effect
+
+		case *ast.FuncLit:
+			// A func literal has no read or write effect
+			// until called, and (most) function calls are
+			// considered to have arbitrary effects.
+			// So, no effect.
+
+		case *ast.CompositeLit:
+			for _, elt := range n.Elts {
+				visitExpr(elt) // note: visits KeyValueExpr
+			}
+
+		case *ast.ParenExpr:
+			visitExpr(n.X)
+
+		case *ast.SelectorExpr:
+			if seln, ok := info.Selections[n]; ok {
+				visitExpr(n.X)
+
+				// See types.SelectionKind for background.
+				switch seln.Kind() {
+				case types.MethodExpr:
+					// A method expression T.f acts like a
+					// reference to a func decl,
+					// so it doesn't read x until called.
+
+				case types.MethodVal, types.FieldVal:
+					// A field or method value selection x.f
+					// reads x if the selection indirects a pointer.
+
+					if indirectSelection(seln) {
+						effect(rinf)
+					}
+				}
+			} else {
+				// qualified identifier: treat like unqualified
+				visitExpr(n.Sel)
+			}
+
+		case *ast.IndexExpr:
+			if tv := info.Types[n.Index]; tv.IsType() {
+				// no effect (G[T] instantiation)
+			} else {
+				visitExpr(n.X)
+				visitExpr(n.Index)
+				switch tv.Type.Underlying().(type) {
+				case *types.Slice, *types.Pointer: // []T, *[n]T (not string, [n]T)
+					effect(rinf) // indirect read of slice/array element
+				}
+			}
+
+		case *ast.IndexListExpr:
+			// no effect (M[K,V] instantiation)
+
+		case *ast.SliceExpr:
+			visitExpr(n.X)
+			visitExpr(n.Low)
+			visitExpr(n.High)
+			visitExpr(n.Max)
+
+		case *ast.TypeAssertExpr:
+			visitExpr(n.X)
+
+		case *ast.CallExpr:
+			if info.Types[n.Fun].IsType() {
+				// conversion T(x)
+				visitExpr(n.Args[0])
+			} else {
+				// call f(args)
+				visitExpr(n.Fun)
+				for i, arg := range n.Args {
+					if i == 0 && info.Types[arg].IsType() {
+						continue // new(T), make(T, n)
+					}
+					visitExpr(arg)
+				}
+
+				// The pure built-ins have no effects beyond
+				// those of their operands (not even memory reads).
+				// All other calls have unknown effects.
+				if !typesinternal.CallsPureBuiltin(info, n) {
+					unknown() // arbitrary effects
+				}
+			}
+
+		case *ast.StarExpr:
+			visitExpr(n.X)
+			effect(rinf) // *ptr load or store depends on state of heap
+
+		case *ast.UnaryExpr: // + - ! ^ & ~ <-
+			visitExpr(n.X)
+			if n.Op == token.ARROW {
+				unknown() // effect: channel receive
+			}
+
+		case *ast.BinaryExpr:
+			visitExpr(n.X)
+			visitExpr(n.Y)
+
+		case *ast.KeyValueExpr:
+			visitExpr(n.Key) // may be a struct field
+			visitExpr(n.Value)
+
+		case *ast.BadExpr:
+			// no effect
+
+		case nil:
+			// optional subtree
+
+		default:
+			// type syntax: unreachable given traversal
+			panic(n)
+		}
+	}
+
+	// visitStmt's result indicates the continuation:
+	// false for return, true for the next statement.
+	//
+	// We could treat return as an unknown, but this way
+	// yields definite effects for simple sequences like
+	// {S1; S2; return}, so unreferenced parameters are
+	// not spuriously added to the effects list, and thus
+	// not spuriously disqualified from elimination.
+	visitStmt = func(n ast.Stmt) bool {
+		switch n := n.(type) {
+		case *ast.DeclStmt:
+			decl := n.Decl.(*ast.GenDecl)
+			for _, spec := range decl.Specs {
+				switch spec := spec.(type) {
+				case *ast.ValueSpec:
+					for _, v := range spec.Values {
+						visitExpr(v)
+					}
+
+				case *ast.TypeSpec:
+					// no effect
+				}
+			}
+
+		case *ast.LabeledStmt:
+			return visitStmt(n.Stmt)
+
+		case *ast.ExprStmt:
+			visitExpr(n.X)
+
+		case *ast.SendStmt:
+			visitExpr(n.Chan)
+			visitExpr(n.Value)
+			unknown() // effect: channel send
+
+		case *ast.IncDecStmt:
+			visitExpr(n.X)
+			unknown() // effect: variable increment
+
+		case *ast.AssignStmt:
+			for _, lhs := range n.Lhs {
+				visitExpr(lhs)
+			}
+			for _, rhs := range n.Rhs {
+				visitExpr(rhs)
+			}
+			for _, lhs := range n.Lhs {
+				id, _ := lhs.(*ast.Ident)
+				if id != nil && id.Name == "_" {
+					continue // blank assign has no effect
+				}
+				if n.Tok == token.DEFINE && id != nil && info.Defs[id] != nil {
+					continue // new var declared by := has no effect
+				}
+				unknown() // assignment to existing var
+				break
+			}
+
+		case *ast.GoStmt:
+			visitExpr(n.Call.Fun)
+			for _, arg := range n.Call.Args {
+				visitExpr(arg)
+			}
+			unknown() // effect: create goroutine
+
+		case *ast.DeferStmt:
+			visitExpr(n.Call.Fun)
+			for _, arg := range n.Call.Args {
+				visitExpr(arg)
+			}
+			unknown() // effect: push defer
+
+		case *ast.ReturnStmt:
+			for _, res := range n.Results {
+				visitExpr(res)
+			}
+			return false
+
+		case *ast.BlockStmt:
+			for _, stmt := range n.List {
+				if !visitStmt(stmt) {
+					return false
+				}
+			}
+
+		case *ast.BranchStmt:
+			unknown() // control flow
+
+		case *ast.IfStmt:
+			visitStmt(n.Init)
+			visitExpr(n.Cond)
+			unknown() // control flow
+
+		case *ast.SwitchStmt:
+			visitStmt(n.Init)
+			visitExpr(n.Tag)
+			unknown() // control flow
+
+		case *ast.TypeSwitchStmt:
+			visitStmt(n.Init)
+			visitStmt(n.Assign)
+			unknown() // control flow
+
+		case *ast.SelectStmt:
+			unknown() // control flow
+
+		case *ast.ForStmt:
+			visitStmt(n.Init)
+			visitExpr(n.Cond)
+			unknown() // control flow
+
+		case *ast.RangeStmt:
+			visitExpr(n.X)
+			unknown() // control flow
+
+		case *ast.EmptyStmt, *ast.BadStmt:
+			// no effect
+
+		case nil:
+			// optional subtree
+
+		default:
+			panic(n)
+		}
+		return true
+	}
+	visitStmt(body)
+
+	return effects
+}
diff --git a/vendor/golang.org/x/tools/internal/refactor/inline/doc.go b/vendor/golang.org/x/tools/internal/refactor/inline/doc.go
new file mode 100644
index 00000000..6bb4cef0
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/refactor/inline/doc.go
@@ -0,0 +1,288 @@
+// Copyright 2023 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 inline implements inlining of Go function calls.
+
+The client provides information about the caller and callee,
+including the source text, syntax tree, and type information, and
+the inliner returns the modified source file for the caller, or an
+error if the inlining operation is invalid (for example because the
+function body refers to names that are inaccessible to the caller).
+
+Although this interface demands more information from the client
+than might seem necessary, it enables smoother integration with
+existing batch and interactive tools that have their own ways of
+managing the processes of reading, parsing, and type-checking
+packages. In particular, this package does not assume that the
+caller and callee belong to the same token.FileSet or
+types.Importer realms.
+
+There are many aspects to a function call. It is the only construct
+that can simultaneously bind multiple variables of different
+explicit types, with implicit assignment conversions. (Neither var
+nor := declarations can do that.) It defines the scope of control
+labels, of return statements, and of defer statements. Arguments
+and results of function calls may be tuples even though tuples are
+not first-class values in Go, and a tuple-valued call expression
+may be "spread" across the argument list of a call or the operands
+of a return statement. All these unique features mean that in the
+general case, not everything that can be expressed by a function
+call can be expressed without one.
+
+So, in general, inlining consists of modifying a function or method
+call expression f(a1, ..., an) so that the name of the function f
+is replaced ("literalized") by a literal copy of the function
+declaration, with free identifiers suitably modified to use the
+locally appropriate identifiers or perhaps constant argument
+values.
+
+Inlining must not change the semantics of the call. Semantics
+preservation is crucial for clients such as codebase maintenance
+tools that automatically inline all calls to designated functions
+on a large scale. Such tools must not introduce subtle behavior
+changes. (Fully inlining a call is dynamically observable using
+reflection over the call stack, but this exception to the rule is
+explicitly allowed.)
+
+In many cases it is possible to entirely replace ("reduce") the
+call by a copy of the function's body in which parameters have been
+replaced by arguments. The inliner supports a number of reduction
+strategies, and we expect this set to grow. Nonetheless, sound
+reduction is surprisingly tricky.
+
+The inliner is in some ways like an optimizing compiler. A compiler
+is considered correct if it doesn't change the meaning of the
+program in translation from source language to target language. An
+optimizing compiler exploits the particulars of the input to
+generate better code, where "better" usually means more efficient.
+When a case is found in which it emits suboptimal code, the
+compiler is improved to recognize more cases, or more rules, and
+more exceptions to rules; this process has no end. Inlining is
+similar except that "better" code means tidier code. The baseline
+translation (literalization) is correct, but there are endless
+rules--and exceptions to rules--by which the output can be
+improved.
+
+The following section lists some of the challenges, and ways in
+which they can be addressed.
+
+  - All effects of the call argument expressions must be preserved,
+    both in their number (they must not be eliminated or repeated),
+    and in their order (both with respect to other arguments, and any
+    effects in the callee function).
+
+    This must be the case even if the corresponding parameters are
+    never referenced, are referenced multiple times, referenced in
+    a different order from the arguments, or referenced within a
+    nested function that may be executed an arbitrary number of
+    times.
+
+    Currently, parameter replacement is not applied to arguments
+    with effects, but with further analysis of the sequence of
+    strict effects within the callee we could relax this constraint.
+
+  - When not all parameters can be substituted by their arguments
+    (e.g. due to possible effects), if the call appears in a
+    statement context, the inliner may introduce a var declaration
+    that declares the parameter variables (with the correct types)
+    and assigns them to their corresponding argument values.
+    The rest of the function body may then follow.
+    For example, the call
+
+    f(1, 2)
+
+    to the function
+
+    func f(x, y int32) { stmts }
+
+    may be reduced to
+
+    { var x, y int32 = 1, 2; stmts }.
+
+    There are many reasons why this is not always possible. For
+    example, true parameters are statically resolved in the same
+    scope, and are dynamically assigned their arguments in
+    parallel; but each spec in a var declaration is statically
+    resolved in sequence and dynamically executed in sequence, so
+    earlier parameters may shadow references in later ones.
+
+  - Even an argument expression as simple as ptr.x may not be
+    referentially transparent, because another argument may have the
+    effect of changing the value of ptr.
+
+    This constraint could be relaxed by some kind of alias or
+    escape analysis that proves that ptr cannot be mutated during
+    the call.
+
+  - Although constants are referentially transparent, as a matter of
+    style we do not wish to duplicate literals that are referenced
+    multiple times in the body because this undoes proper factoring.
+    Also, string literals may be arbitrarily large.
+
+  - If the function body consists of statements other than just
+    "return expr", in some contexts it may be syntactically
+    impossible to reduce the call. Consider:
+
+    if x := f(); cond { ... }
+
+    Go has no equivalent to Lisp's progn or Rust's blocks,
+    nor ML's let expressions (let param = arg in body);
+    its closest equivalent is func(param){body}(arg).
+    Reduction strategies must therefore consider the syntactic
+    context of the call.
+
+    In such situations we could work harder to extract a statement
+    context for the call, by transforming it to:
+
+    { x := f(); if cond { ... } }
+
+  - Similarly, without the equivalent of Rust-style blocks and
+    first-class tuples, there is no general way to reduce a call
+    to a function such as
+
+    func(params)(args)(results) { stmts; return expr }
+
+    to an expression such as
+
+    { var params = args; stmts; expr }
+
+    or even a statement such as
+
+    results = { var params = args; stmts; expr }
+
+    Consequently the declaration and scope of the result variables,
+    and the assignment and control-flow implications of the return
+    statement, must be dealt with by cases.
+
+  - A standalone call statement that calls a function whose body is
+    "return expr" cannot be simply replaced by the body expression
+    if it is not itself a call or channel receive expression; it is
+    necessary to explicitly discard the result using "_ = expr".
+
+    Similarly, if the body is a call expression, only calls to some
+    built-in functions with no result (such as copy or panic) are
+    permitted as statements, whereas others (such as append) return
+    a result that must be used, even if just by discarding.
+
+  - If a parameter or result variable is updated by an assignment
+    within the function body, it cannot always be safely replaced
+    by a variable in the caller. For example, given
+
+    func f(a int) int { a++; return a }
+
+    The call y = f(x) cannot be replaced by { x++; y = x } because
+    this would change the value of the caller's variable x.
+    Only if the caller is finished with x is this safe.
+
+    A similar argument applies to parameter or result variables
+    that escape: by eliminating a variable, inlining would change
+    the identity of the variable that escapes.
+
+  - If the function body uses 'defer' and the inlined call is not a
+    tail-call, inlining may delay the deferred effects.
+
+  - Because the scope of a control label is the entire function, a
+    call cannot be reduced if the caller and callee have intersecting
+    sets of control labels. (It is possible to α-rename any
+    conflicting ones, but our colleagues building C++ refactoring
+    tools report that, when tools must choose new identifiers, they
+    generally do a poor job.)
+
+  - Given
+
+    func f() uint8 { return 0 }
+
+    var x any = f()
+
+    reducing the call to var x any = 0 is unsound because it
+    discards the implicit conversion to uint8. We may need to make
+    each argument-to-parameter conversion explicit if the types
+    differ. Assignments to variadic parameters may need to
+    explicitly construct a slice.
+
+    An analogous problem applies to the implicit assignments in
+    return statements:
+
+    func g() any { return f() }
+
+    Replacing the call f() with 0 would silently lose a
+    conversion to uint8 and change the behavior of the program.
+
+  - When inlining a call f(1, x, g()) where those parameters are
+    unreferenced, we should be able to avoid evaluating 1 and x
+    since they are pure and thus have no effect. But x may be the
+    last reference to a local variable in the caller, so removing
+    it would cause a compilation error. Parameter substitution must
+    avoid making the caller's local variables unreferenced (or must
+    be prepared to eliminate the declaration too---this is where an
+    iterative framework for simplification would really help).
+
+  - An expression such as s[i] may be valid if s and i are
+    variables but invalid if either or both of them are constants.
+    For example, a negative constant index s[-1] is always out of
+    bounds, and even a non-negative constant index may be out of
+    bounds depending on the particular string constant (e.g.
+    "abc"[4]).
+
+    So, if a parameter participates in any expression that is
+    subject to additional compile-time checks when its operands are
+    constant, it may be unsafe to substitute that parameter by a
+    constant argument value (#62664).
+
+More complex callee functions are inlinable with more elaborate and
+invasive changes to the statements surrounding the call expression.
+
+TODO(adonovan): future work:
+
+  - Handle more of the above special cases by careful analysis,
+    thoughtful factoring of the large design space, and thorough
+    test coverage.
+
+  - Compute precisely (not conservatively) when parameter
+    substitution would remove the last reference to a caller local
+    variable, and blank out the local instead of retreating from
+    the substitution.
+
+  - Afford the client more control such as a limit on the total
+    increase in line count, or a refusal to inline using the
+    general approach (replacing name by function literal). This
+    could be achieved by returning metadata alongside the result
+    and having the client conditionally discard the change.
+
+  - Support inlining of generic functions, replacing type parameters
+    by their instantiations.
+
+  - Support inlining of calls to function literals ("closures").
+    But note that the existing algorithm makes widespread assumptions
+    that the callee is a package-level function or method.
+
+  - Eliminate explicit conversions of "untyped" literals inserted
+    conservatively when they are redundant. For example, the
+    conversion int32(1) is redundant when this value is used only as a
+    slice index; but it may be crucial if it is used in x := int32(1)
+    as it changes the type of x, which may have further implications.
+    The conversions may also be important to the falcon analysis.
+
+  - Allow non-'go' build systems such as Bazel/Blaze a chance to
+    decide whether an import is accessible using logic other than
+    "/internal/" path segments. This could be achieved by returning
+    the list of added import paths instead of a text diff.
+
+  - Inlining a function from another module may change the
+    effective version of the Go language spec that governs it. We
+    should probably make the client responsible for rejecting
+    attempts to inline from newer callees to older callers, since
+    there's no way for this package to access module versions.
+
+  - Use an alternative implementation of the import-organizing
+    operation that doesn't require operating on a complete file
+    (and reformatting). Then return the results in a higher-level
+    form as a set of import additions and deletions plus a single
+    diff that encloses the call expression. This interface could
+    perhaps be implemented atop imports.Process by post-processing
+    its result to obtain the abstract import changes and discarding
+    its formatted output.
+*/
+package inline
diff --git a/vendor/golang.org/x/tools/internal/refactor/inline/escape.go b/vendor/golang.org/x/tools/internal/refactor/inline/escape.go
new file mode 100644
index 00000000..45cce11a
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/refactor/inline/escape.go
@@ -0,0 +1,102 @@
+// Copyright 2023 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 inline
+
+import (
+	"fmt"
+	"go/ast"
+	"go/token"
+	"go/types"
+)
+
+// escape implements a simple "address-taken" escape analysis. It
+// calls f for each local variable that appears on the left side of an
+// assignment (escapes=false) or has its address taken (escapes=true).
+// The initialization of a variable by its declaration does not count
+// as an assignment.
+func escape(info *types.Info, root ast.Node, f func(v *types.Var, escapes bool)) {
+
+	// lvalue is called for each address-taken expression or LHS of assignment.
+	// Supported forms are: x, (x), x[i], x.f, *x, T{}.
+	var lvalue func(e ast.Expr, escapes bool)
+	lvalue = func(e ast.Expr, escapes bool) {
+		switch e := e.(type) {
+		case *ast.Ident:
+			if v, ok := info.Uses[e].(*types.Var); ok {
+				if !isPkgLevel(v) {
+					f(v, escapes)
+				}
+			}
+		case *ast.ParenExpr:
+			lvalue(e.X, escapes)
+		case *ast.IndexExpr:
+			// TODO(adonovan): support generics without assuming e.X has a core type.
+			// Consider:
+			//
+			// func Index[T interface{ [3]int | []int }](t T, i int) *int {
+			//     return &t[i]
+			// }
+			//
+			// We must traverse the normal terms and check
+			// whether any of them is an array.
+			//
+			// We assume TypeOf returns non-nil.
+			if _, ok := info.TypeOf(e.X).Underlying().(*types.Array); ok {
+				lvalue(e.X, escapes) // &a[i] on array
+			}
+		case *ast.SelectorExpr:
+			// We assume TypeOf returns non-nil.
+			if _, ok := info.TypeOf(e.X).Underlying().(*types.Struct); ok {
+				lvalue(e.X, escapes) // &s.f on struct
+			}
+		case *ast.StarExpr:
+			// *ptr indirects an existing pointer
+		case *ast.CompositeLit:
+			// &T{...} creates a new variable
+		default:
+			panic(fmt.Sprintf("&x on %T", e)) // unreachable in well-typed code
+		}
+	}
+
+	// Search function body for operations &x, x.f(), x++, and x = y
+	// where x is a parameter. Each of these treats x as an address.
+	ast.Inspect(root, func(n ast.Node) bool {
+		switch n := n.(type) {
+		case *ast.UnaryExpr:
+			if n.Op == token.AND {
+				lvalue(n.X, true) // &x
+			}
+
+		case *ast.CallExpr:
+			// implicit &x in method call x.f(),
+			// where x has type T and method is (*T).f
+			if sel, ok := n.Fun.(*ast.SelectorExpr); ok {
+				if seln, ok := info.Selections[sel]; ok &&
+					seln.Kind() == types.MethodVal &&
+					isPointer(seln.Obj().Type().Underlying().(*types.Signature).Recv().Type()) {
+					tArg, indirect := effectiveReceiver(seln)
+					if !indirect && !isPointer(tArg) {
+						lvalue(sel.X, true) // &x.f
+					}
+				}
+			}
+
+		case *ast.AssignStmt:
+			for _, lhs := range n.Lhs {
+				if id, ok := lhs.(*ast.Ident); ok &&
+					info.Defs[id] != nil &&
+					n.Tok == token.DEFINE {
+					// declaration: doesn't count
+				} else {
+					lvalue(lhs, false)
+				}
+			}
+
+		case *ast.IncDecStmt:
+			lvalue(n.X, false)
+		}
+		return true
+	})
+}
diff --git a/vendor/golang.org/x/tools/internal/refactor/inline/falcon.go b/vendor/golang.org/x/tools/internal/refactor/inline/falcon.go
new file mode 100644
index 00000000..884a807a
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/refactor/inline/falcon.go
@@ -0,0 +1,889 @@
+// Copyright 2023 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 inline
+
+// This file defines the callee side of the "fallible constant" analysis.
+
+import (
+	"fmt"
+	"go/ast"
+	"go/constant"
+	"go/format"
+	"go/token"
+	"go/types"
+	"slices"
+	"strconv"
+	"strings"
+
+	"golang.org/x/tools/go/types/typeutil"
+	"golang.org/x/tools/internal/typeparams"
+)
+
+// falconResult is the result of the analysis of the callee.
+type falconResult struct {
+	Types       []falconType // types for falcon constraint environment
+	Constraints []string     // constraints (Go expressions) on values of fallible constants
+}
+
+// A falconType specifies the name and underlying type of a synthetic
+// defined type for use in falcon constraints.
+//
+// Unique types from callee code are bijectively mapped onto falcon
+// types so that constraints are independent of callee type
+// information but preserve type equivalence classes.
+//
+// Fresh names are deliberately obscure to avoid shadowing even if a
+// callee parameter has a name like "int" or "any".
+type falconType struct {
+	Name string
+	Kind types.BasicKind // string/number/bool
+}
+
+// falcon identifies "fallible constant" expressions, which are
+// expressions that may fail to compile if one or more of their
+// operands is changed from non-constant to constant.
+//
+// Consider:
+//
+//	func sub(s string, i, j int) string { return s[i:j] }
+//
+// If parameters are replaced by constants, the compiler is
+// required to perform these additional checks:
+//
+//   - if i is constant, 0 <= i.
+//   - if s and i are constant, i <= len(s).
+//   - ditto for j.
+//   - if i and j are constant, i <= j.
+//
+// s[i:j] is thus a "fallible constant" expression dependent on {s, i,
+// j}. Each falcon creates a set of conditional constraints across one
+// or more parameter variables.
+//
+//   - When inlining a call such as sub("abc", -1, 2), the parameter i
+//     cannot be eliminated by substitution as its argument value is
+//     negative.
+//
+//   - When inlining sub("", 2, 1), all three parameters cannot be
+//     simultaneously eliminated by substitution without violating i
+//     <= len(s) and j <= len(s), but the parameters i and j could be
+//     safely eliminated without s.
+//
+// Parameters that cannot be eliminated must remain non-constant,
+// either in the form of a binding declaration:
+//
+//	{ var i int = -1; return "abc"[i:2] }
+//
+// or a parameter of a literalization:
+//
+//	func (i int) string { return "abc"[i:2] }(-1)
+//
+// These example expressions are obviously doomed to fail at run
+// time, but in realistic cases such expressions are dominated by
+// appropriate conditions that make them reachable only when safe:
+//
+//	if 0 <= i && i <= j && j <= len(s) { _ = s[i:j] }
+//
+// (In principle a more sophisticated inliner could entirely eliminate
+// such unreachable blocks based on the condition being always-false
+// for the given parameter substitution, but this is tricky to do safely
+// because the type-checker considers only a single configuration.
+// Consider: if runtime.GOOS == "linux" { ... }.)
+//
+// We believe this is an exhaustive list of "fallible constant" operations:
+//
+//   - switch z { case x: case y } 	// duplicate case values
+//   - s[i], s[i:j], s[i:j:k]		// index out of bounds (0 <= i <= j <= k <= len(s))
+//   - T{x: 0}				// index out of bounds, duplicate index
+//   - x/y, x%y, x/=y, x%=y		// integer division by zero; minint/-1 overflow
+//   - x+y, x-y, x*y			// arithmetic overflow
+//   - x< 1 {
+		var elts []ast.Expr
+		for _, elem := range elems {
+			elts = append(elts, &ast.KeyValueExpr{
+				Key:   elem,
+				Value: makeIntLit(0),
+			})
+		}
+		st.emit(&ast.CompositeLit{
+			Type: typ,
+			Elts: elts,
+		})
+	}
+}
+
+// -- traversal --
+
+// The traversal functions scan the callee body for expressions that
+// are not constant but would become constant if the parameter vars
+// were redeclared as constants, and emits for each one a constraint
+// (a Go expression) with the property that it will not type-check
+// (using types.CheckExpr) if the particular argument values are
+// unsuitable.
+//
+// These constraints are checked by Inline with the actual
+// constant argument values. Violations cause it to reject
+// parameters as candidates for substitution.
+
+func (st *falconState) stmt(s ast.Stmt) {
+	ast.Inspect(s, func(n ast.Node) bool {
+		switch n := n.(type) {
+		case ast.Expr:
+			_ = st.expr(n)
+			return false // skip usual traversal
+
+		case *ast.AssignStmt:
+			switch n.Tok {
+			case token.QUO_ASSIGN, token.REM_ASSIGN:
+				// x /= y
+				// Possible "integer division by zero"
+				// Emit constraint: 1/y.
+				_ = st.expr(n.Lhs[0])
+				kY := st.expr(n.Rhs[0])
+				if kY, ok := kY.(ast.Expr); ok {
+					op := token.QUO
+					if n.Tok == token.REM_ASSIGN {
+						op = token.REM
+					}
+					st.emit(&ast.BinaryExpr{
+						Op: op,
+						X:  makeIntLit(1),
+						Y:  kY,
+					})
+				}
+				return false // skip usual traversal
+			}
+
+		case *ast.SwitchStmt:
+			if n.Init != nil {
+				st.stmt(n.Init)
+			}
+			tBool := types.Type(types.Typ[types.Bool])
+			tagType := tBool // default: true
+			if n.Tag != nil {
+				st.expr(n.Tag)
+				tagType = st.info.TypeOf(n.Tag)
+			}
+
+			// Possible "duplicate case value".
+			// Emit constraint map[T]int{v1: 0, ..., vN:0}
+			// to ensure all maybe-constant case values are unique
+			// (unless switch tag is boolean, which is relaxed).
+			var unique []ast.Expr
+			for _, clause := range n.Body.List {
+				clause := clause.(*ast.CaseClause)
+				for _, caseval := range clause.List {
+					if k := st.expr(caseval); k != nil {
+						unique = append(unique, st.toExpr(k))
+					}
+				}
+				for _, stmt := range clause.Body {
+					st.stmt(stmt)
+				}
+			}
+			if unique != nil && !types.Identical(tagType.Underlying(), tBool) {
+				tname := st.any
+				if !types.IsInterface(tagType) {
+					tname = st.typename(tagType)
+				}
+				t := &ast.MapType{
+					Key:   makeIdent(tname),
+					Value: makeIdent(st.int),
+				}
+				st.emitUnique(t, unique)
+			}
+		}
+		return true
+	})
+}
+
+// fieldTypes visits the .Type of each field in the list.
+func (st *falconState) fieldTypes(fields *ast.FieldList) {
+	if fields != nil {
+		for _, field := range fields.List {
+			_ = st.expr(field.Type)
+		}
+	}
+}
+
+// expr visits the expression (or type) and returns a
+// non-nil result if the expression is constant or would
+// become constant if all suitable function parameters were
+// redeclared as constants.
+//
+// If the expression is constant, st.expr returns its type
+// and value (types.TypeAndValue). If the expression would
+// become constant, st.expr returns an ast.Expr tree whose
+// leaves are literals and parameter references, and whose
+// interior nodes are operations that may become constant,
+// such as -x, x+y, f(x), and T(x). We call these would-be
+// constant expressions "fallible constants", since they may
+// fail to type-check for some values of x, i, and j. (We
+// refer to the non-nil cases collectively as "maybe
+// constant", and the nil case as "definitely non-constant".)
+//
+// As a side effect, st.expr emits constraints for each
+// fallible constant expression; this is its main purpose.
+//
+// Consequently, st.expr must visit the entire subtree so
+// that all necessary constraints are emitted. It may not
+// short-circuit the traversal when it encounters a constant
+// subexpression as constants may contain arbitrary other
+// syntax that may impose constraints. Consider (as always)
+// this contrived but legal example of a type parameter (!)
+// that contains statement syntax:
+//
+//	func f[T [unsafe.Sizeof(func() { stmts })]int]()
+//
+// There is no need to emit constraints for (e.g.) s[i] when s
+// and i are already constants, because we know the expression
+// is sound, but it is sometimes easier to emit these
+// redundant constraints than to avoid them.
+func (st *falconState) expr(e ast.Expr) (res any) { // = types.TypeAndValue | ast.Expr
+	tv := st.info.Types[e]
+	if tv.Value != nil {
+		// A constant value overrides any other result.
+		defer func() { res = tv }()
+	}
+
+	switch e := e.(type) {
+	case *ast.Ident:
+		if v, ok := st.info.Uses[e].(*types.Var); ok {
+			if _, ok := st.params[v]; ok && isBasic(v.Type(), types.IsConstType) {
+				return e // reference to constable parameter
+			}
+		}
+		// (References to *types.Const are handled by the defer.)
+
+	case *ast.BasicLit:
+		// constant
+
+	case *ast.ParenExpr:
+		return st.expr(e.X)
+
+	case *ast.FuncLit:
+		_ = st.expr(e.Type)
+		st.stmt(e.Body)
+		// definitely non-constant
+
+	case *ast.CompositeLit:
+		// T{k: v, ...}, where T ∈ {array,*array,slice,map},
+		// imposes a constraint that all constant k are
+		// distinct and, for arrays [n]T, within range 0-n.
+		//
+		// Types matter, not just values. For example,
+		// an interface-keyed map may contain keys
+		// that are numerically equal so long as they
+		// are of distinct types. For example:
+		//
+		//   type myint int
+		//   map[any]bool{1: true, 1:        true} // error: duplicate key
+		//   map[any]bool{1: true, int16(1): true} // ok
+		//   map[any]bool{1: true, myint(1): true} // ok
+		//
+		// This can be asserted by emitting a
+		// constraint of the form T{k1: 0, ..., kN: 0}.
+		if e.Type != nil {
+			_ = st.expr(e.Type)
+		}
+		t := types.Unalias(typeparams.Deref(tv.Type))
+		ct := typeparams.CoreType(t)
+		var mapKeys []ast.Expr // map key expressions; must be distinct if constant
+		for _, elt := range e.Elts {
+			if kv, ok := elt.(*ast.KeyValueExpr); ok {
+				if is[*types.Map](ct) {
+					if k := st.expr(kv.Key); k != nil {
+						mapKeys = append(mapKeys, st.toExpr(k))
+					}
+				}
+				_ = st.expr(kv.Value)
+			} else {
+				_ = st.expr(elt)
+			}
+		}
+		if len(mapKeys) > 0 {
+			// Inlining a map literal may replace variable key expressions by constants.
+			// All such constants must have distinct values.
+			// (Array and slice literals do not permit non-constant keys.)
+			t := ct.(*types.Map)
+			var typ ast.Expr
+			if types.IsInterface(t.Key()) {
+				typ = &ast.MapType{
+					Key:   makeIdent(st.any),
+					Value: makeIdent(st.int),
+				}
+			} else {
+				typ = &ast.MapType{
+					Key:   makeIdent(st.typename(t.Key())),
+					Value: makeIdent(st.int),
+				}
+			}
+			st.emitUnique(typ, mapKeys)
+		}
+		// definitely non-constant
+
+	case *ast.SelectorExpr:
+		_ = st.expr(e.X)
+		_ = st.expr(e.Sel)
+		// The defer is sufficient to handle
+		// qualified identifiers (pkg.Const).
+		// All other cases are definitely non-constant.
+
+	case *ast.IndexExpr:
+		if tv.IsType() {
+			// type C[T]
+			_ = st.expr(e.X)
+			_ = st.expr(e.Index)
+		} else {
+			// term x[i]
+			//
+			// Constraints (if x is slice/string/array/*array, not map):
+			// - i >= 0
+			//     if i is a fallible constant
+			// - i < len(x)
+			//     if x is array/*array and
+			//     i is a fallible constant;
+			//  or if s is a string and both i,
+			//     s are maybe-constants,
+			//     but not both are constants.
+			kX := st.expr(e.X)
+			kI := st.expr(e.Index)
+			if kI != nil && !is[*types.Map](st.info.TypeOf(e.X).Underlying()) {
+				if kI, ok := kI.(ast.Expr); ok {
+					st.emitNonNegative(kI)
+				}
+				// Emit constraint to check indices against known length.
+				// TODO(adonovan): factor with SliceExpr logic.
+				var x ast.Expr
+				if kX != nil {
+					// string
+					x = st.toExpr(kX)
+				} else if arr, ok := typeparams.CoreType(typeparams.Deref(st.info.TypeOf(e.X))).(*types.Array); ok {
+					// array, *array
+					x = &ast.CompositeLit{
+						Type: &ast.ArrayType{
+							Len: makeIntLit(arr.Len()),
+							Elt: makeIdent(st.int),
+						},
+					}
+				}
+				if x != nil {
+					st.emit(&ast.IndexExpr{
+						X:     x,
+						Index: st.toExpr(kI),
+					})
+				}
+			}
+		}
+		// definitely non-constant
+
+	case *ast.SliceExpr:
+		// x[low:high:max]
+		//
+		// Emit non-negative constraints for each index,
+		// plus low <= high <= max <= len(x)
+		// for each pair that are maybe-constant
+		// but not definitely constant.
+
+		kX := st.expr(e.X)
+		var kLow, kHigh, kMax any
+		if e.Low != nil {
+			kLow = st.expr(e.Low)
+			if kLow != nil {
+				if kLow, ok := kLow.(ast.Expr); ok {
+					st.emitNonNegative(kLow)
+				}
+			}
+		}
+		if e.High != nil {
+			kHigh = st.expr(e.High)
+			if kHigh != nil {
+				if kHigh, ok := kHigh.(ast.Expr); ok {
+					st.emitNonNegative(kHigh)
+				}
+				if kLow != nil {
+					st.emitMonotonic(st.toExpr(kLow), st.toExpr(kHigh))
+				}
+			}
+		}
+		if e.Max != nil {
+			kMax = st.expr(e.Max)
+			if kMax != nil {
+				if kMax, ok := kMax.(ast.Expr); ok {
+					st.emitNonNegative(kMax)
+				}
+				if kHigh != nil {
+					st.emitMonotonic(st.toExpr(kHigh), st.toExpr(kMax))
+				}
+			}
+		}
+
+		// Emit constraint to check indices against known length.
+		var x ast.Expr
+		if kX != nil {
+			// string
+			x = st.toExpr(kX)
+		} else if arr, ok := typeparams.CoreType(typeparams.Deref(st.info.TypeOf(e.X))).(*types.Array); ok {
+			// array, *array
+			x = &ast.CompositeLit{
+				Type: &ast.ArrayType{
+					Len: makeIntLit(arr.Len()),
+					Elt: makeIdent(st.int),
+				},
+			}
+		}
+		if x != nil {
+			// Avoid slice[::max] if kHigh is nonconstant (nil).
+			high, max := st.toExpr(kHigh), st.toExpr(kMax)
+			if high == nil {
+				high = max // => slice[:max:max]
+			}
+			st.emit(&ast.SliceExpr{
+				X:    x,
+				Low:  st.toExpr(kLow),
+				High: high,
+				Max:  max,
+			})
+		}
+		// definitely non-constant
+
+	case *ast.TypeAssertExpr:
+		_ = st.expr(e.X)
+		if e.Type != nil {
+			_ = st.expr(e.Type)
+		}
+
+	case *ast.CallExpr:
+		_ = st.expr(e.Fun)
+		if tv, ok := st.info.Types[e.Fun]; ok && tv.IsType() {
+			// conversion T(x)
+			//
+			// Possible "value out of range".
+			kX := st.expr(e.Args[0])
+			if kX != nil && isBasic(tv.Type, types.IsConstType) {
+				conv := convert(makeIdent(st.typename(tv.Type)), st.toExpr(kX))
+				if is[ast.Expr](kX) {
+					st.emit(conv)
+				}
+				return conv
+			}
+			return nil // definitely non-constant
+		}
+
+		// call f(x)
+
+		all := true // all args are possibly-constant
+		kArgs := make([]ast.Expr, len(e.Args))
+		for i, arg := range e.Args {
+			if kArg := st.expr(arg); kArg != nil {
+				kArgs[i] = st.toExpr(kArg)
+			} else {
+				all = false
+			}
+		}
+
+		// Calls to built-ins with fallibly constant arguments
+		// may become constant. All other calls are either
+		// constant or non-constant
+		if id, ok := e.Fun.(*ast.Ident); ok && all && tv.Value == nil {
+			if builtin, ok := st.info.Uses[id].(*types.Builtin); ok {
+				switch builtin.Name() {
+				case "len", "imag", "real", "complex", "min", "max":
+					return &ast.CallExpr{
+						Fun:      id,
+						Args:     kArgs,
+						Ellipsis: e.Ellipsis,
+					}
+				}
+			}
+		}
+
+	case *ast.StarExpr: // *T, *ptr
+		_ = st.expr(e.X)
+
+	case *ast.UnaryExpr:
+		// + - ! ^ & <- ~
+		//
+		// Possible "negation of minint".
+		// Emit constraint: -x
+		kX := st.expr(e.X)
+		if kX != nil && !is[types.TypeAndValue](kX) {
+			if e.Op == token.SUB {
+				st.emit(&ast.UnaryExpr{
+					Op: e.Op,
+					X:  st.toExpr(kX),
+				})
+			}
+
+			return &ast.UnaryExpr{
+				Op: e.Op,
+				X:  st.toExpr(kX),
+			}
+		}
+
+	case *ast.BinaryExpr:
+		kX := st.expr(e.X)
+		kY := st.expr(e.Y)
+		switch e.Op {
+		case token.QUO, token.REM:
+			// x/y, x%y
+			//
+			// Possible "integer division by zero" or
+			// "minint / -1" overflow.
+			// Emit constraint: x/y or 1/y
+			if kY != nil {
+				if kX == nil {
+					kX = makeIntLit(1)
+				}
+				st.emit(&ast.BinaryExpr{
+					Op: e.Op,
+					X:  st.toExpr(kX),
+					Y:  st.toExpr(kY),
+				})
+			}
+
+		case token.ADD, token.SUB, token.MUL:
+			// x+y, x-y, x*y
+			//
+			// Possible "arithmetic overflow".
+			// Emit constraint: x+y
+			if kX != nil && kY != nil {
+				st.emit(&ast.BinaryExpr{
+					Op: e.Op,
+					X:  st.toExpr(kX),
+					Y:  st.toExpr(kY),
+				})
+			}
+
+		case token.SHL, token.SHR:
+			// x << y, x >> y
+			//
+			// Possible "constant shift too large".
+			// Either operand may be too large individually,
+			// and they may be too large together.
+			// Emit constraint:
+			//    x << y (if both maybe-constant)
+			//    x << 0 (if y is non-constant)
+			//    1 << y (if x is non-constant)
+			if kX != nil || kY != nil {
+				x := st.toExpr(kX)
+				if x == nil {
+					x = makeIntLit(1)
+				}
+				y := st.toExpr(kY)
+				if y == nil {
+					y = makeIntLit(0)
+				}
+				st.emit(&ast.BinaryExpr{
+					Op: e.Op,
+					X:  x,
+					Y:  y,
+				})
+			}
+
+		case token.LSS, token.GTR, token.EQL, token.NEQ, token.LEQ, token.GEQ:
+			// < > == != <= <=
+			//
+			// A "x cmp y" expression with constant operands x, y is
+			// itself constant, but I can't see how a constant bool
+			// could be fallible: the compiler doesn't reject duplicate
+			// boolean cases in a switch, presumably because boolean
+			// switches are less like n-way branches and more like
+			// sequential if-else chains with possibly overlapping
+			// conditions; and there is (sadly) no way to convert a
+			// boolean constant to an int constant.
+		}
+		if kX != nil && kY != nil {
+			return &ast.BinaryExpr{
+				Op: e.Op,
+				X:  st.toExpr(kX),
+				Y:  st.toExpr(kY),
+			}
+		}
+
+	// types
+	//
+	// We need to visit types (and even type parameters)
+	// in order to reach all the places where things could go wrong:
+	//
+	// 	const (
+	// 		s = ""
+	// 		i = 0
+	// 	)
+	// 	type C[T [unsafe.Sizeof(func() { _ = s[i] })]int] bool
+
+	case *ast.IndexListExpr:
+		_ = st.expr(e.X)
+		for _, expr := range e.Indices {
+			_ = st.expr(expr)
+		}
+
+	case *ast.Ellipsis:
+		if e.Elt != nil {
+			_ = st.expr(e.Elt)
+		}
+
+	case *ast.ArrayType:
+		if e.Len != nil {
+			_ = st.expr(e.Len)
+		}
+		_ = st.expr(e.Elt)
+
+	case *ast.StructType:
+		st.fieldTypes(e.Fields)
+
+	case *ast.FuncType:
+		st.fieldTypes(e.TypeParams)
+		st.fieldTypes(e.Params)
+		st.fieldTypes(e.Results)
+
+	case *ast.InterfaceType:
+		st.fieldTypes(e.Methods)
+
+	case *ast.MapType:
+		_ = st.expr(e.Key)
+		_ = st.expr(e.Value)
+
+	case *ast.ChanType:
+		_ = st.expr(e.Value)
+	}
+	return
+}
+
+// toExpr converts the result of visitExpr to a falcon expression.
+// (We don't do this in visitExpr as we first need to discriminate
+// constants from maybe-constants.)
+func (st *falconState) toExpr(x any) ast.Expr {
+	switch x := x.(type) {
+	case nil:
+		return nil
+
+	case types.TypeAndValue:
+		lit := makeLiteral(x.Value)
+		if !isBasic(x.Type, types.IsUntyped) {
+			// convert to "typed" type
+			lit = &ast.CallExpr{
+				Fun:  makeIdent(st.typename(x.Type)),
+				Args: []ast.Expr{lit},
+			}
+		}
+		return lit
+
+	case ast.Expr:
+		return x
+
+	default:
+		panic(x)
+	}
+}
+
+func makeLiteral(v constant.Value) ast.Expr {
+	switch v.Kind() {
+	case constant.Bool:
+		// Rather than refer to the true or false built-ins,
+		// which could be shadowed by poorly chosen parameter
+		// names, we use 0 == 0 for true and 0 != 0 for false.
+		op := token.EQL
+		if !constant.BoolVal(v) {
+			op = token.NEQ
+		}
+		return &ast.BinaryExpr{
+			Op: op,
+			X:  makeIntLit(0),
+			Y:  makeIntLit(0),
+		}
+
+	case constant.String:
+		return &ast.BasicLit{
+			Kind:  token.STRING,
+			Value: v.ExactString(),
+		}
+
+	case constant.Int:
+		return &ast.BasicLit{
+			Kind:  token.INT,
+			Value: v.ExactString(),
+		}
+
+	case constant.Float:
+		return &ast.BasicLit{
+			Kind:  token.FLOAT,
+			Value: v.ExactString(),
+		}
+
+	case constant.Complex:
+		// The components could be float or int.
+		y := makeLiteral(constant.Imag(v))
+		y.(*ast.BasicLit).Value += "i" // ugh
+		if re := constant.Real(v); !consteq(re, kZeroInt) {
+			// complex: x + yi
+			y = &ast.BinaryExpr{
+				Op: token.ADD,
+				X:  makeLiteral(re),
+				Y:  y,
+			}
+		}
+		return y
+
+	default:
+		panic(v.Kind())
+	}
+}
+
+func makeIntLit(x int64) *ast.BasicLit {
+	return &ast.BasicLit{
+		Kind:  token.INT,
+		Value: strconv.FormatInt(x, 10),
+	}
+}
+
+func isBasic(t types.Type, info types.BasicInfo) bool {
+	basic, ok := t.Underlying().(*types.Basic)
+	return ok && basic.Info()&info != 0
+}
diff --git a/vendor/golang.org/x/tools/internal/refactor/inline/inline.go b/vendor/golang.org/x/tools/internal/refactor/inline/inline.go
new file mode 100644
index 00000000..b329ab6f
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/refactor/inline/inline.go
@@ -0,0 +1,3557 @@
+// Copyright 2023 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 inline
+
+import (
+	"bytes"
+	"fmt"
+	"go/ast"
+	"go/constant"
+	"go/format"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"maps"
+	pathpkg "path"
+	"reflect"
+	"slices"
+	"strings"
+
+	"golang.org/x/tools/go/ast/astutil"
+	"golang.org/x/tools/go/types/typeutil"
+	internalastutil "golang.org/x/tools/internal/astutil"
+	"golang.org/x/tools/internal/astutil/free"
+	"golang.org/x/tools/internal/packagepath"
+	"golang.org/x/tools/internal/refactor"
+	"golang.org/x/tools/internal/typeparams"
+	"golang.org/x/tools/internal/typesinternal"
+	"golang.org/x/tools/internal/versions"
+)
+
+// A Caller describes the function call and its enclosing context.
+//
+// The client is responsible for populating this struct and passing it to Inline.
+type Caller struct {
+	Fset  *token.FileSet
+	Types *types.Package
+	Info  *types.Info
+	File  *ast.File
+	Call  *ast.CallExpr
+
+	// CountUses is an optional optimized computation of
+	// the number of times pkgname appears in Info.Uses.
+	CountUses func(pkgname *types.PkgName) int
+
+	path          []ast.Node    // path from call to root of file syntax tree
+	enclosingFunc *ast.FuncDecl // top-level function/method enclosing the call, if any
+}
+
+type logger = func(string, ...any)
+
+// Options specifies parameters affecting the inliner algorithm.
+// All fields are optional.
+type Options struct {
+	Logf          logger // log output function, records decision-making process
+	IgnoreEffects bool   // ignore potential side effects of arguments (unsound)
+}
+
+// Result holds the result of code transformation.
+type Result struct {
+	Edits       []refactor.Edit // edits around CallExpr and imports
+	Literalized bool            // chosen strategy replaced callee() with func(){...}()
+	BindingDecl bool            // transformation added "var params = args" declaration
+}
+
+// Inline inlines the called function (callee) into the function call (caller)
+// and returns the updated, formatted content of the caller source file.
+//
+// Inline does not mutate any public fields of Caller or Callee.
+func Inline(caller *Caller, callee *Callee, opts *Options) (*Result, error) {
+	copy := *opts // shallow copy
+	opts = ©
+	// Set default options.
+	if opts.Logf == nil {
+		opts.Logf = func(string, ...any) {}
+	}
+
+	st := &state{
+		caller: caller,
+		callee: callee,
+		opts:   opts,
+	}
+	return st.inline()
+}
+
+// state holds the working state of the inliner.
+type state struct {
+	caller *Caller
+	callee *Callee
+	opts   *Options
+}
+
+func (st *state) inline() (*Result, error) {
+	logf, caller, callee := st.opts.Logf, st.caller, st.callee
+
+	logf("inline %s @ %v",
+		debugFormatNode(caller.Fset, caller.Call),
+		caller.Fset.PositionFor(caller.Call.Lparen, false))
+
+	if ast.IsGenerated(caller.File) {
+		return nil, fmt.Errorf("cannot inline calls from generated files")
+	}
+
+	res, err := st.inlineCall()
+	if err != nil {
+		return nil, err
+	}
+
+	// Replace the call (or some node that encloses it) by new syntax.
+	assert(res.old != nil, "old is nil")
+	assert(res.new != nil, "new is nil")
+
+	// A single return operand inlined to a unary
+	// expression context may need parens. Otherwise:
+	//    func two() int { return 1+1 }
+	//    print(-two())  =>  print(-1+1) // oops!
+	//
+	// Usually it is not necessary to insert ParenExprs
+	// as the formatter is smart enough to insert them as
+	// needed by the context. But the res.{old,new}
+	// substitution is done by formatting res.new in isolation
+	// and then splicing its text over res.old, so the
+	// formatter doesn't see the parent node and cannot do
+	// the right thing. (One solution would be to always
+	// format the enclosing node of old, but that requires
+	// non-lossy comment handling, #20744.)
+	//
+	// So, we must analyze the call's context
+	// to see whether ambiguity is possible.
+	// For example, if the context is x[y:z], then
+	// the x subtree is subject to precedence ambiguity
+	// (replacing x by p+q would give p+q[y:z] which is wrong)
+	// but the y and z subtrees are safe.
+	if new, ok := res.new.(ast.Expr); ok {
+		parent := caller.path[slices.Index(caller.path, res.old)+1]
+		res.new = internalastutil.MaybeParenthesize(parent, res.old.(ast.Expr), new)
+	}
+
+	// Some reduction strategies return a new block holding the
+	// callee's statements. The block's braces may be elided when
+	// there is no conflict between names declared in the block
+	// with those declared by the parent block, and no risk of
+	// a caller's goto jumping forward across a declaration.
+	//
+	// This elision is only safe when the ExprStmt is beneath a
+	// BlockStmt, CaseClause.Body, or CommClause.Body;
+	// (see "statement theory").
+	//
+	// The inlining analysis may have already determined that eliding braces is
+	// safe. Otherwise, we analyze its safety here.
+	elideBraces := res.elideBraces
+	if !elideBraces {
+		if newBlock, ok := res.new.(*ast.BlockStmt); ok {
+			i := slices.Index(caller.path, res.old)
+			parent := caller.path[i+1]
+			var body []ast.Stmt
+			switch parent := parent.(type) {
+			case *ast.BlockStmt:
+				body = parent.List
+			case *ast.CommClause:
+				body = parent.Body
+			case *ast.CaseClause:
+				body = parent.Body
+			}
+			if body != nil {
+				callerNames := declares(body)
+
+				// If BlockStmt is a function body,
+				// include its receiver, params, and results.
+				addFieldNames := func(fields *ast.FieldList) {
+					if fields != nil {
+						for _, field := range fields.List {
+							for _, id := range field.Names {
+								callerNames[id.Name] = true
+							}
+						}
+					}
+				}
+				switch f := caller.path[i+2].(type) {
+				case *ast.FuncDecl:
+					addFieldNames(f.Recv)
+					addFieldNames(f.Type.Params)
+					addFieldNames(f.Type.Results)
+				case *ast.FuncLit:
+					addFieldNames(f.Type.Params)
+					addFieldNames(f.Type.Results)
+				}
+
+				if len(callerLabels(caller.path)) > 0 {
+					// TODO(adonovan): be more precise and reject
+					// only forward gotos across the inlined block.
+					logf("keeping block braces: caller uses control labels")
+				} else if intersects(declares(newBlock.List), callerNames) {
+					logf("keeping block braces: avoids name conflict")
+				} else {
+					elideBraces = true
+				}
+			}
+		}
+	}
+
+	var edits []refactor.Edit
+
+	// Format the cloned callee.
+	{
+		// TODO(adonovan): might it make more sense to use
+		// callee.Fset when formatting res.new?
+		// The new tree is a mix of (cloned) caller nodes for
+		// the argument expressions and callee nodes for the
+		// function body. In essence the question is: which
+		// is more likely to have comments?
+		// Usually the callee body will be larger and more
+		// statement-heavy than the arguments, but a
+		// strategy may widen the scope of the replacement
+		// (res.old) from CallExpr to, say, its enclosing
+		// block, so the caller nodes dominate.
+		// Precise comment handling would make this a
+		// non-issue. Formatting wouldn't really need a
+		// FileSet at all.
+
+		var out bytes.Buffer
+		if elideBraces {
+			for i, stmt := range res.new.(*ast.BlockStmt).List {
+				if i > 0 {
+					out.WriteByte('\n')
+				}
+				if err := format.Node(&out, caller.Fset, stmt); err != nil {
+					return nil, err
+				}
+			}
+		} else {
+			if err := format.Node(&out, caller.Fset, res.new); err != nil {
+				return nil, err
+			}
+		}
+
+		edits = append(edits, refactor.Edit{
+			Pos:     res.old.Pos(),
+			End:     res.old.End(),
+			NewText: out.Bytes(),
+		})
+	}
+
+	// Add new imports.
+	//
+	// It's possible that not all are needed (e.g. for type names
+	// that melted away), but we'll let the client (such as an
+	// analysis driver) clean it up since it must remove unused
+	// imports anyway.
+	for _, imp := range res.newImports {
+		// Check that the new imports are accessible.
+		if !packagepath.CanImport(caller.Types.Path(), imp.path) {
+			return nil, fmt.Errorf("can't inline function %v as its body refers to inaccessible package %q", callee, imp.path)
+		}
+
+		// We've already validated the import, so we call
+		// AddImportEdits directly to compute the edit.
+		name := ""
+		if imp.explicit {
+			name = imp.name
+		}
+		edits = append(edits, refactor.AddImportEdits(caller.File, name, imp.path)...)
+	}
+
+	literalized := false
+	if call, ok := res.new.(*ast.CallExpr); ok && is[*ast.FuncLit](call.Fun) {
+		literalized = true
+	}
+
+	// Delete imports referenced only by caller.Call.Fun.
+	//
+	// It's ambiguous to let the client (e.g. analysis driver)
+	// remove unneeded imports in this case because it is common
+	// to inlining a call from "dir1/a".F to "dir2/a".F, which
+	// leaves two imports of packages named 'a', both providing a.F.
+	//
+	// However, the only two import deletion tools at our disposal
+	// are astutil.DeleteNamedImport, which mutates the AST, and
+	// refactor.Delete{Spec,Decl}, which need a Cursor. So we need
+	// to reinvent the wheel here.
+	for _, oldImport := range res.oldImports {
+		spec := oldImport.spec
+
+		// Include adjacent comments.
+		pos := spec.Pos()
+		if doc := spec.Doc; doc != nil {
+			pos = doc.Pos()
+		}
+		end := spec.End()
+		if doc := spec.Comment; doc != nil {
+			end = doc.End()
+		}
+
+		// Find the enclosing import decl.
+		// If it's paren-less, we must delete it too.
+		for _, decl := range caller.File.Decls {
+			decl, ok := decl.(*ast.GenDecl)
+			if !(ok && decl.Tok == token.IMPORT) {
+				break // stop at first non-import decl
+			}
+			if internalastutil.NodeContainsPos(decl, spec.Pos()) && !decl.Rparen.IsValid() {
+				// Include adjacent comments.
+				pos = decl.Pos()
+				if doc := decl.Doc; doc != nil {
+					pos = doc.Pos()
+				}
+				end = decl.End()
+				break
+			}
+		}
+
+		edits = append(edits, refactor.Edit{
+			Pos: pos,
+			End: end,
+		})
+	}
+
+	return &Result{
+		Edits:       edits,
+		Literalized: literalized,
+		BindingDecl: res.bindingDecl,
+	}, nil
+}
+
+// An oldImport is an import that will be deleted from the caller file.
+type oldImport struct {
+	pkgName *types.PkgName
+	spec    *ast.ImportSpec
+}
+
+// A newImport is an import that will be added to the caller file.
+type newImport struct {
+	name     string
+	path     string
+	explicit bool // use name as ImportSpec.Name
+}
+
+// importState tracks information about imports.
+type importState struct {
+	logf       func(string, ...any)
+	caller     *Caller
+	importMap  map[string][]string // from package paths in the caller's file to local names
+	newImports []newImport         // for references to free names in callee; to be added to the file
+	oldImports []oldImport         // referenced only by caller.Call.Fun; to be removed from the file
+}
+
+// newImportState returns an importState with initial information about the caller's imports.
+func newImportState(logf func(string, ...any), caller *Caller, callee *gobCallee) *importState {
+	// For simplicity we ignore existing dot imports, so that a qualified
+	// identifier (QI) in the callee is always represented by a QI in the caller,
+	// allowing us to treat a QI like a selection on a package name.
+	ist := &importState{
+		logf:      logf,
+		caller:    caller,
+		importMap: make(map[string][]string),
+	}
+
+	// Provide an inefficient default implementation of CountUses.
+	// (Ideally clients amortize this for the entire package.)
+	countUses := caller.CountUses
+	if countUses == nil {
+		uses := make(map[*types.PkgName]int)
+		for _, obj := range caller.Info.Uses {
+			if pkgname, ok := obj.(*types.PkgName); ok {
+				uses[pkgname]++
+			}
+		}
+		countUses = func(pkgname *types.PkgName) int {
+			return uses[pkgname]
+		}
+	}
+
+	for _, imp := range caller.File.Imports {
+		if pkgName, ok := importedPkgName(caller.Info, imp); ok &&
+			pkgName.Name() != "." &&
+			pkgName.Name() != "_" {
+
+			// If the import's sole use is in caller.Call.Fun of the form p.F(...),
+			// where p.F is a qualified identifier, the p import may not be
+			// necessary.
+			//
+			// Only the qualified identifier case matters, as other references to
+			// imported package names in the Call.Fun expression (e.g.
+			// x.after(3*time.Second).f() or time.Second.String()) will remain after
+			// inlining, as arguments.
+			//
+			// If that is the case, proactively check if any of the callee FreeObjs
+			// need this import. Doing so eagerly simplifies the resulting logic.
+			needed := true
+			if sel, ok := ast.Unparen(caller.Call.Fun).(*ast.SelectorExpr); ok &&
+				is[*ast.Ident](sel.X) &&
+				caller.Info.Uses[sel.X.(*ast.Ident)] == pkgName &&
+				countUses(pkgName) == 1 {
+				needed = false // no longer needed by caller
+				// Check to see if any of the inlined free objects need this package.
+				for _, obj := range callee.FreeObjs {
+					if obj.PkgPath == pkgName.Imported().Path() && obj.Shadow[pkgName.Name()] == 0 {
+						needed = true // needed by callee
+						break
+					}
+				}
+			}
+
+			// Exclude imports not needed by the caller or callee after inlining; the second
+			// return value holds these.
+			if needed {
+				path := pkgName.Imported().Path()
+				ist.importMap[path] = append(ist.importMap[path], pkgName.Name())
+			} else {
+				ist.oldImports = append(ist.oldImports, oldImport{pkgName: pkgName, spec: imp})
+			}
+		}
+	}
+	return ist
+}
+
+// importName finds an existing import name to use in a particular shadowing
+// context. It is used to determine the set of new imports in
+// localName, and is also used for writing out names in inlining
+// strategies below.
+func (i *importState) importName(pkgPath string, shadow shadowMap) string {
+	for _, name := range i.importMap[pkgPath] {
+		// Check that either the import preexisted, or that it was newly added
+		// (no PkgName) but is not shadowed, either in the callee (shadows) or
+		// caller (caller.lookup).
+		if shadow[name] == 0 {
+			found := i.caller.lookup(name)
+			if is[*types.PkgName](found) || found == nil {
+				return name
+			}
+		}
+	}
+	return ""
+}
+
+// findNewLocalName returns a new local package name to use in a particular shadowing context.
+// It considers the existing local name used by the callee, or construct a new local name
+// based on the package name.
+func (i *importState) findNewLocalName(pkgName, calleePkgName string, shadow shadowMap) string {
+	newlyAdded := func(name string) bool {
+		return slices.ContainsFunc(i.newImports, func(n newImport) bool { return n.name == name })
+	}
+
+	// shadowedInCaller reports whether a candidate package name
+	// already refers to a declaration in the caller.
+	shadowedInCaller := func(name string) bool {
+		obj := i.caller.lookup(name)
+		if obj == nil {
+			return false
+		}
+		// If obj will be removed, the name is available.
+		return !slices.ContainsFunc(i.oldImports, func(o oldImport) bool { return o.pkgName == obj })
+	}
+
+	// import added by callee
+	//
+	// Try to preserve the local package name used by the callee first.
+	//
+	// If that is shadowed, choose a local package name based on last segment of
+	// package path plus, if needed, a numeric suffix to ensure uniqueness.
+	//
+	// "init" is not a legal PkgName.
+	if shadow[calleePkgName] == 0 && !shadowedInCaller(calleePkgName) && !newlyAdded(calleePkgName) && calleePkgName != "init" {
+		return calleePkgName
+	}
+
+	base := pkgName
+	name := base
+	for n := 0; shadow[name] != 0 || shadowedInCaller(name) || newlyAdded(name) || name == "init"; n++ {
+		name = fmt.Sprintf("%s%d", base, n)
+	}
+
+	return name
+}
+
+// localName returns the local name for a given imported package path,
+// adding one if it doesn't exists.
+func (i *importState) localName(pkgPath, pkgName, calleePkgName string, shadow shadowMap) string {
+	// Does an import already exist that works in this shadowing context?
+	if name := i.importName(pkgPath, shadow); name != "" {
+		return name
+	}
+
+	name := i.findNewLocalName(pkgName, calleePkgName, shadow)
+	i.logf("adding import %s %q", name, pkgPath)
+	// Use explicit pkgname (out of necessity) when it differs from the declared name,
+	// or (for good style) when it differs from base(pkgpath).
+	i.newImports = append(i.newImports, newImport{
+		name:     name,
+		path:     pkgPath,
+		explicit: name != pkgName || name != pathpkg.Base(pkgPath),
+	})
+	i.importMap[pkgPath] = append(i.importMap[pkgPath], name)
+	return name
+}
+
+type inlineCallResult struct {
+	newImports []newImport // to add
+	oldImports []oldImport // to remove
+
+	// If elideBraces is set, old is an ast.Stmt and new is an ast.BlockStmt to
+	// be spliced in. This allows the inlining analysis to assert that inlining
+	// the block is OK; if elideBraces is unset and old is an ast.Stmt and new is
+	// an ast.BlockStmt, braces may still be elided if the post-processing
+	// analysis determines that it is safe to do so.
+	//
+	// Ideally, it would not be necessary for the inlining analysis to "reach
+	// through" to the post-processing pass in this way. Instead, inlining could
+	// just set old to be an ast.BlockStmt and rewrite the entire BlockStmt, but
+	// unfortunately in order to preserve comments, it is important that inlining
+	// replace as little syntax as possible.
+	elideBraces bool
+	bindingDecl bool     // transformation inserted "var params = args" declaration
+	old, new    ast.Node // e.g. replace call expr by callee function body expression
+}
+
+// inlineCall returns a pair of an old node (the call, or something
+// enclosing it) and a new node (its replacement, which may be a
+// combination of caller, callee, and new nodes), along with the set
+// of new imports needed.
+//
+// TODO(adonovan): rethink the 'result' interface. The assumption of a
+// one-to-one replacement seems fragile. One can easily imagine the
+// transformation replacing the call and adding new variable
+// declarations, for example, or replacing a call statement by zero or
+// many statements.)
+// NOTE(rfindley): we've sort-of done this, with the 'elideBraces' flag that
+// allows inlining a statement list. However, due to loss of comments, more
+// sophisticated rewrites are challenging.
+//
+// TODO(rfindley): see if we can reduce the amount of comment lossiness by
+// using printer.CommentedNode, which has been useful elsewhere.
+//
+// TODO(rfindley): inlineCall is getting very long, and very stateful, making
+// it very hard to read. The following refactoring may improve readability and
+// maintainability:
+//   - Rename 'state' to 'callsite', since that is what it encapsulates.
+//   - Add results of pre-processing analysis into the callsite struct, such as
+//     the effective importMap, new/old imports, arguments, etc. Essentially
+//     anything that resulted from initial analysis of the call site, and which
+//     may be useful to inlining strategies.
+//   - Delegate this call site analysis to a constructor or initializer, such
+//     as 'analyzeCallsite', so that it does not consume bandwidth in the
+//     'inlineCall' logical flow.
+//   - Once analyzeCallsite returns, the callsite is immutable, much in the
+//     same way as the Callee and Caller are immutable.
+//   - Decide on a standard interface for strategies (and substrategies), such
+//     that they may be delegated to a separate method on callsite.
+//
+// In this way, the logical flow of inline call will clearly follow the
+// following structure:
+//  1. Analyze the call site.
+//  2. Try strategies, in order, until one succeeds.
+//  3. Process the results.
+//
+// If any expensive analysis may be avoided by earlier strategies, it can be
+// encapsulated in its own type and passed to subsequent strategies.
+func (st *state) inlineCall() (*inlineCallResult, error) {
+	logf, caller, callee := st.opts.Logf, st.caller, &st.callee.impl
+
+	checkInfoFields(caller.Info)
+
+	// Inlining of dynamic calls is not currently supported,
+	// even for local closure calls. (This would be a lot of work.)
+	calleeSymbol := typeutil.StaticCallee(caller.Info, caller.Call)
+	if calleeSymbol == nil {
+		// e.g. interface method
+		return nil, fmt.Errorf("cannot inline: not a static function call")
+	}
+
+	// Reject cross-package inlining if callee has
+	// free references to unexported symbols.
+	samePkg := caller.Types.Path() == callee.PkgPath
+	if !samePkg && len(callee.Unexported) > 0 {
+		return nil, fmt.Errorf("cannot inline call to %s because body refers to non-exported %s",
+			callee.Name, callee.Unexported[0])
+	}
+
+	// Reject cross-file inlining if callee requires a newer dialect of Go (#75726).
+	// (Versions default to types.Config.GoVersion, which is unset in many tests,
+	// though should be populated by an analysis driver.)
+	callerGoVersion := caller.Info.FileVersions[caller.File]
+	if callerGoVersion != "" && callee.GoVersion != "" && versions.Before(callerGoVersion, callee.GoVersion) {
+		return nil, fmt.Errorf("cannot inline call to %s (declared using %s) into a file using %s",
+			callee.Name, callee.GoVersion, callerGoVersion)
+	}
+
+	// -- analyze callee's free references in caller context --
+
+	// Compute syntax path enclosing Call, innermost first (Path[0]=Call),
+	// and outermost enclosing function, if any.
+	caller.path, _ = astutil.PathEnclosingInterval(caller.File, caller.Call.Pos(), caller.Call.End())
+	for _, n := range caller.path {
+		if decl, ok := n.(*ast.FuncDecl); ok {
+			caller.enclosingFunc = decl
+			break
+		}
+	}
+
+	// If call is within a function, analyze all its
+	// local vars for the "single assignment" property.
+	// (Taking the address &v counts as a potential assignment.)
+	var assign1 func(v *types.Var) bool // reports whether v a single-assignment local var
+	{
+		updatedLocals := make(map[*types.Var]bool)
+		if caller.enclosingFunc != nil {
+			escape(caller.Info, caller.enclosingFunc, func(v *types.Var, _ bool) {
+				updatedLocals[v] = true
+			})
+			logf("multiple-assignment vars: %v", updatedLocals)
+		}
+		assign1 = func(v *types.Var) bool { return !updatedLocals[v] }
+	}
+
+	// Extract information about the caller's imports.
+	istate := newImportState(logf, caller, callee)
+
+	// Compute the renaming of the callee's free identifiers.
+	objRenames, err := st.renameFreeObjs(istate)
+	if err != nil {
+		return nil, err
+	}
+
+	res := &inlineCallResult{
+		newImports: istate.newImports,
+		oldImports: istate.oldImports,
+	}
+
+	// Parse callee function declaration.
+	calleeFset, calleeDecl, err := parseCompact(callee.Content)
+	if err != nil {
+		return nil, err // "can't happen"
+	}
+
+	// replaceCalleeID replaces an identifier in the callee. See [replacer] for
+	// more detailed semantics.
+	replaceCalleeID := func(offset int, repl ast.Expr, unpackVariadic bool) {
+		path, id := findIdent(calleeDecl, calleeDecl.Pos()+token.Pos(offset))
+		logf("- replace id %q @ #%d to %q", id.Name, offset, debugFormatNode(calleeFset, repl))
+		// Replace f([]T{a, b, c}...) with f(a, b, c).
+		if lit, ok := repl.(*ast.CompositeLit); ok && unpackVariadic && len(path) > 0 {
+			if call, ok := last(path).(*ast.CallExpr); ok &&
+				call.Ellipsis.IsValid() &&
+				id == last(call.Args) {
+
+				call.Args = append(call.Args[:len(call.Args)-1], lit.Elts...)
+				call.Ellipsis = token.NoPos
+				return
+			}
+		}
+		if len(path) > 0 {
+			repl = internalastutil.MaybeParenthesize(last(path), id, repl)
+		}
+		replaceNode(calleeDecl, id, repl)
+	}
+
+	// Generate replacements for each free identifier.
+	// (The same tree may be spliced in multiple times, resulting in a DAG.)
+	for _, ref := range callee.FreeRefs {
+		if repl := objRenames[ref.Object]; repl != nil {
+			replaceCalleeID(ref.Offset, repl, false)
+		}
+	}
+
+	// Gather the effective call arguments, including the receiver.
+	// Later, elements will be eliminated (=> nil) by parameter substitution.
+	args, err := st.arguments(caller, calleeDecl, assign1)
+	if err != nil {
+		return nil, err // e.g. implicit field selection cannot be made explicit
+	}
+
+	// Gather effective parameter tuple, including the receiver if any.
+	// Simplify variadic parameters to slices (in all cases but one).
+	var params []*parameter // including receiver; nil => parameter substituted
+	{
+		sig := calleeSymbol.Type().(*types.Signature)
+		if sig.Recv() != nil {
+			params = append(params, ¶meter{
+				obj:       sig.Recv(),
+				fieldType: calleeDecl.Recv.List[0].Type,
+				info:      callee.Params[0],
+			})
+		}
+
+		// Flatten the list of syntactic types.
+		var types []ast.Expr
+		for _, field := range calleeDecl.Type.Params.List {
+			if field.Names == nil {
+				types = append(types, field.Type)
+			} else {
+				for range field.Names {
+					types = append(types, field.Type)
+				}
+			}
+		}
+
+		for i := 0; i < sig.Params().Len(); i++ {
+			params = append(params, ¶meter{
+				obj:       sig.Params().At(i),
+				fieldType: types[i],
+				info:      callee.Params[len(params)],
+			})
+		}
+
+		// Variadic function?
+		//
+		// There are three possible types of call:
+		// - ordinary f(a1, ..., aN)
+		// - ellipsis f(a1, ..., slice...)
+		// - spread   f(recv?, g()) where g() is a tuple.
+		// The first two are desugared to non-variadic calls
+		// with an ordinary slice parameter;
+		// the third is tricky and cannot be reduced, and (if
+		// a receiver is present) cannot even be literalized.
+		// Fortunately it is vanishingly rare.
+		//
+		// TODO(adonovan): extract this to a function.
+		if sig.Variadic() {
+			lastParam := last(params)
+			if len(args) > 0 && last(args).spread {
+				// spread call to variadic: tricky
+				lastParam.variadic = true
+			} else {
+				// ordinary/ellipsis call to variadic
+
+				// simplify decl: func(T...) -> func([]T)
+				lastParamField := last(calleeDecl.Type.Params.List)
+				lastParamField.Type = &ast.ArrayType{
+					Elt: lastParamField.Type.(*ast.Ellipsis).Elt,
+				}
+
+				if caller.Call.Ellipsis.IsValid() {
+					// ellipsis call: f(slice...) -> f(slice)
+					// nop
+				} else {
+					// ordinary call: f(a1, ... aN) -> f([]T{a1, ..., aN})
+					//
+					// Substitution of []T{...} in the callee body may lead to
+					// g([]T{a1, ..., aN}...), which we simplify to g(a1, ..., an)
+					// later; see replaceCalleeID.
+					n := len(params) - 1
+					ordinary, extra := args[:n], args[n:]
+					var elts []ast.Expr
+					freevars := make(map[string]bool)
+					pure, effects := true, false
+					for _, arg := range extra {
+						elts = append(elts, arg.expr)
+						pure = pure && arg.pure
+						effects = effects || arg.effects
+						maps.Copy(freevars, arg.freevars)
+					}
+					args = append(ordinary, &argument{
+						expr: &ast.CompositeLit{
+							Type: lastParamField.Type,
+							Elts: elts,
+						},
+						typ:        lastParam.obj.Type(),
+						constant:   nil,
+						pure:       pure,
+						effects:    effects,
+						duplicable: false,
+						freevars:   freevars,
+						variadic:   true,
+					})
+				}
+			}
+		}
+	}
+
+	// Substitute type parameters in calleeDecl AST with type arguments from the
+	// call, and synchronize the parameter metadata.
+	{
+		typeArgs := st.typeArguments(caller.Call)
+		if len(typeArgs) != len(callee.TypeParams) {
+			return nil, fmt.Errorf("cannot inline: type parameter inference is not yet supported")
+		}
+		if err := substituteTypeParams(logf, callee.TypeParams, typeArgs, replaceCalleeID); err != nil {
+			return nil, err
+		}
+		// Synchronize the parameters' type pointers with the mutated calleeDecl.
+		syncParamFieldTypes(calleeDecl, params)
+	}
+
+	// Log effective arguments.
+	for i, arg := range args {
+		logf("arg #%d: %s pure=%t effects=%t duplicable=%t free=%v type=%v",
+			i, debugFormatNode(caller.Fset, arg.expr),
+			arg.pure, arg.effects, arg.duplicable, arg.freevars, arg.typ)
+	}
+
+	// Note: computation below should be expressed in terms of
+	// the args and params slices, not the raw material.
+
+	// Perform parameter substitution.
+	// May eliminate some elements of params/args.
+	substitute(logf, caller, params, args, callee.Effects, callee.Falcon, replaceCalleeID)
+
+	// Update the callee's signature syntax.
+	updateCalleeParams(calleeDecl, params)
+
+	// Create a var (param = arg; ...) decl for use by some strategies.
+	bindingDecl := createBindingDecl(logf, caller, args, calleeDecl, callee.Results)
+
+	var remainingArgs []ast.Expr
+	for _, arg := range args {
+		if arg != nil {
+			remainingArgs = append(remainingArgs, arg.expr)
+		}
+	}
+
+	// -- let the inlining strategies begin --
+	//
+	// When we commit to a strategy, we log a message of the form:
+	//
+	//   "strategy: reduce expr-context call to { return expr }"
+	//
+	// This is a terse way of saying:
+	//
+	//    we plan to reduce a call
+	//    that appears in expression context
+	//    to a function whose body is of the form { return expr }
+
+	// TODO(adonovan): split this huge function into a sequence of
+	// function calls with an error sentinel that means "try the
+	// next strategy", and make sure each strategy writes to the
+	// log the reason it didn't match.
+
+	// Special case: eliminate a call to a function whose body is empty.
+	// (=> callee has no results and caller is a statement.)
+	//
+	//    func f(params) {}
+	//    f(args)
+	//    => _, _ = args
+	//
+	if len(calleeDecl.Body.List) == 0 {
+		logf("strategy: reduce call to empty body")
+
+		// Evaluate the arguments for effects and delete the call entirely.
+		// Note(golang/go#71486): stmt can be nil if the call is in a go or defer
+		// statement.
+		// TODO: discard go or defer statements as well.
+		if stmt := callStmt(caller.path, false); stmt != nil {
+			res.old = stmt
+			if nargs := len(remainingArgs); nargs > 0 {
+				// Emit "_, _ = args" to discard results.
+
+				// TODO(adonovan): if args is the []T{a1, ..., an}
+				// literal synthesized during variadic simplification,
+				// consider unwrapping it to its (pure) elements.
+				// Perhaps there's no harm doing this for any slice literal.
+
+				// Make correction for spread calls
+				// f(g()) or recv.f(g()) where g() is a tuple.
+				if last := last(args); last != nil && last.spread {
+					nspread := last.typ.(*types.Tuple).Len()
+					if len(args) > 1 { // [recv, g()]
+						// A single AssignStmt cannot discard both, so use a 2-spec var decl.
+						res.new = &ast.GenDecl{
+							Tok: token.VAR,
+							Specs: []ast.Spec{
+								&ast.ValueSpec{
+									Names:  []*ast.Ident{makeIdent("_")},
+									Values: []ast.Expr{args[0].expr},
+								},
+								&ast.ValueSpec{
+									Names:  blanks[*ast.Ident](nspread),
+									Values: []ast.Expr{args[1].expr},
+								},
+							},
+						}
+						return res, nil
+					}
+
+					// Sole argument is spread call.
+					nargs = nspread
+				}
+
+				res.new = &ast.AssignStmt{
+					Lhs: blanks[ast.Expr](nargs),
+					Tok: token.ASSIGN,
+					Rhs: remainingArgs,
+				}
+
+			} else {
+				// No remaining arguments: delete call statement entirely
+				res.new = &ast.EmptyStmt{}
+			}
+			return res, nil
+		}
+	}
+
+	// If all parameters have been substituted and no result
+	// variable is referenced, we don't need a binding decl.
+	// This may enable better reduction strategies.
+	allResultsUnreferenced := forall(callee.Results, func(i int, r *paramInfo) bool { return len(r.Refs) == 0 })
+	needBindingDecl := !allResultsUnreferenced ||
+		exists(params, func(i int, p *parameter) bool { return p != nil })
+
+	// The two strategies below overlap for a tail call of {return exprs}:
+	// The expr-context reduction is nice because it keeps the
+	// caller's return stmt and merely switches its operand,
+	// without introducing a new block, but it doesn't work with
+	// implicit return conversions.
+	//
+	// TODO(adonovan): unify these cases more cleanly, allowing return-
+	// operand replacement and implicit conversions, by adding
+	// conversions around each return operand (if not a spread return).
+
+	// Special case: call to { return exprs }.
+	//
+	// Reduces to:
+	//	    { var (bindings); _, _ = exprs }
+	//     or   _, _ = exprs
+	//     or   expr
+	//
+	// If:
+	// - the body is just "return expr" with trivial implicit conversions,
+	//   or the caller's return type matches the callee's,
+	// - all parameters and result vars can be eliminated
+	//   or replaced by a binding decl,
+	// then the call expression can be replaced by the
+	// callee's body expression, suitably substituted.
+	if len(calleeDecl.Body.List) == 1 &&
+		is[*ast.ReturnStmt](calleeDecl.Body.List[0]) &&
+		len(calleeDecl.Body.List[0].(*ast.ReturnStmt).Results) > 0 { // not a bare return
+		results := calleeDecl.Body.List[0].(*ast.ReturnStmt).Results
+
+		parent, grandparent := callContext(caller.path)
+
+		// statement context
+		if stmt, ok := parent.(*ast.ExprStmt); ok &&
+			(!needBindingDecl || bindingDecl != nil) {
+			logf("strategy: reduce stmt-context call to { return exprs }")
+			clearPositions(calleeDecl.Body)
+
+			if callee.ValidForCallStmt {
+				logf("callee body is valid as statement")
+				// Inv: len(results) == 1
+				if !needBindingDecl {
+					// Reduces to: expr
+					res.old = caller.Call
+					res.new = results[0]
+				} else {
+					// Reduces to: { var (bindings); expr }
+					res.bindingDecl = true
+					res.old = stmt
+					res.new = &ast.BlockStmt{
+						List: []ast.Stmt{
+							bindingDecl.stmt,
+							&ast.ExprStmt{X: results[0]},
+						},
+					}
+				}
+			} else {
+				logf("callee body is not valid as statement")
+				// The call is a standalone statement, but the
+				// callee body is not suitable as a standalone statement
+				// (f() or <-ch), explicitly discard the results:
+				// Reduces to: _, _ = exprs
+				discard := &ast.AssignStmt{
+					Lhs: blanks[ast.Expr](callee.NumResults),
+					Tok: token.ASSIGN,
+					Rhs: results,
+				}
+				res.old = stmt
+				if !needBindingDecl {
+					// Reduces to: _, _ = exprs
+					res.new = discard
+				} else {
+					// Reduces to: { var (bindings); _, _ = exprs }
+					res.bindingDecl = true
+					res.new = &ast.BlockStmt{
+						List: []ast.Stmt{
+							bindingDecl.stmt,
+							discard,
+						},
+					}
+				}
+			}
+			return res, nil
+		}
+
+		// Assignment context.
+		//
+		// If there is no binding decl, or if the binding decl declares no names,
+		// an assignment a, b := f() can be reduced to a, b := x, y.
+		if stmt, ok := parent.(*ast.AssignStmt); ok &&
+			is[*ast.BlockStmt](grandparent) &&
+			(!needBindingDecl || (bindingDecl != nil && len(bindingDecl.names) == 0)) {
+
+			// Reduces to: { var (bindings); lhs... := rhs... }
+			if newStmts, ok := st.assignStmts(stmt, results, istate.importName); ok {
+				logf("strategy: reduce assign-context call to { return exprs }")
+
+				clearPositions(calleeDecl.Body)
+
+				block := &ast.BlockStmt{
+					List: newStmts,
+				}
+				if needBindingDecl {
+					res.bindingDecl = true
+					block.List = prepend(bindingDecl.stmt, block.List...)
+				}
+
+				// assignStmts does not introduce new bindings, and replacing an
+				// assignment only works if the replacement occurs in the same scope.
+				// Therefore, we must ensure that braces are elided.
+				res.elideBraces = true
+				res.old = stmt
+				res.new = block
+				return res, nil
+			}
+		}
+
+		// expression context
+		if !needBindingDecl {
+			clearPositions(calleeDecl.Body)
+
+			anyNonTrivialReturns := hasNonTrivialReturn(callee.Returns)
+
+			if callee.NumResults == 1 {
+				logf("strategy: reduce expr-context call to { return expr }")
+				// (includes some simple tail-calls)
+
+				// Make implicit return conversion explicit.
+				if anyNonTrivialReturns {
+					results[0] = convert(calleeDecl.Type.Results.List[0].Type, results[0])
+				}
+
+				res.old = caller.Call
+				res.new = results[0]
+				return res, nil
+
+			} else if !anyNonTrivialReturns {
+				logf("strategy: reduce spread-context call to { return expr }")
+				// There is no general way to reify conversions in a spread
+				// return, hence the requirement above.
+				//
+				// TODO(adonovan): allow this reduction when no
+				// conversion is required by the context.
+
+				// The call returns multiple results but is
+				// not a standalone call statement. It must
+				// be the RHS of a spread assignment:
+				//   var x, y  = f()
+				//       x, y := f()
+				//       x, y  = f()
+				// or the sole argument to a spread call:
+				//        printf(f())
+				// or spread return statement:
+				//        return f()
+				res.old = parent
+				switch context := parent.(type) {
+				case *ast.AssignStmt:
+					// Inv: the call must be in Rhs[0], not Lhs.
+					assign := shallowCopy(context)
+					assign.Rhs = results
+					res.new = assign
+				case *ast.ValueSpec:
+					// Inv: the call must be in Values[0], not Names.
+					spec := shallowCopy(context)
+					spec.Values = results
+					res.new = spec
+				case *ast.CallExpr:
+					// Inv: the call must be in Args[0], not Fun.
+					call := shallowCopy(context)
+					call.Args = results
+					res.new = call
+				case *ast.ReturnStmt:
+					// Inv: the call must be Results[0].
+					ret := shallowCopy(context)
+					ret.Results = results
+					res.new = ret
+				default:
+					return nil, fmt.Errorf("internal error: unexpected context %T for spread call", context)
+				}
+				return res, nil
+			}
+		}
+	}
+
+	// Special case: tail-call.
+	//
+	// Inlining:
+	//         return f(args)
+	// where:
+	//         func f(params) (results) { body }
+	// reduces to:
+	//         { var (bindings); body }
+	//         { body }
+	// so long as:
+	// - all parameters can be eliminated or replaced by a binding decl,
+	// - call is a tail-call;
+	// - all returns in body have trivial result conversions,
+	//   or the caller's return type matches the callee's,
+	// - there is no label conflict;
+	// - no result variable is referenced by name,
+	//   or implicitly by a bare return.
+	//
+	// The body may use defer, arbitrary control flow, and
+	// multiple returns.
+	//
+	// TODO(adonovan): add a strategy for a 'void tail
+	// call', i.e. a call statement prior to an (explicit
+	// or implicit) return.
+	parent, _ := callContext(caller.path)
+	if ret, ok := parent.(*ast.ReturnStmt); ok &&
+		len(ret.Results) == 1 &&
+		tailCallSafeReturn(caller, calleeSymbol, callee) &&
+		!callee.HasBareReturn &&
+		(!needBindingDecl || bindingDecl != nil) &&
+		!hasLabelConflict(caller.path, callee.Labels) &&
+		allResultsUnreferenced {
+		logf("strategy: reduce tail-call")
+		body := calleeDecl.Body
+		clearPositions(body)
+		if needBindingDecl {
+			res.bindingDecl = true
+			body.List = prepend(bindingDecl.stmt, body.List...)
+		}
+		res.old = ret
+		res.new = body
+		return res, nil
+	}
+
+	// Special case: call to void function
+	//
+	// Inlining:
+	//         f(args)
+	// where:
+	//	   func f(params) { stmts }
+	// reduces to:
+	//         { var (bindings); stmts }
+	//         { stmts }
+	// so long as:
+	// - callee is a void function (no returns)
+	// - callee does not use defer
+	// - there is no label conflict between caller and callee
+	// - all parameters and result vars can be eliminated
+	//   or replaced by a binding decl,
+	// - caller ExprStmt is in unrestricted statement context.
+	if stmt := callStmt(caller.path, true); stmt != nil &&
+		(!needBindingDecl || bindingDecl != nil) &&
+		!callee.HasDefer &&
+		!hasLabelConflict(caller.path, callee.Labels) &&
+		len(callee.Returns) == 0 {
+		logf("strategy: reduce stmt-context call to { stmts }")
+		body := calleeDecl.Body
+		var repl ast.Stmt = body
+		clearPositions(repl)
+		if needBindingDecl {
+			body.List = prepend(bindingDecl.stmt, body.List...)
+		}
+		res.old = stmt
+		res.new = repl
+		return res, nil
+	}
+
+	// TODO(adonovan): parameterless call to { stmts; return expr }
+	// from one of these contexts:
+	//    x, y     = f()
+	//    x, y    := f()
+	//    var x, y = f()
+	// =>
+	//    var (x T1, y T2); { stmts; x, y = expr }
+	//
+	// Because the params are no longer declared simultaneously
+	// we need to check that (for example) x ∉ freevars(T2),
+	// in addition to the usual checks for arg/result conversions,
+	// complex control, etc.
+	// Also test cases where expr is an n-ary call (spread returns).
+
+	// Literalization isn't quite infallible.
+	// Consider a spread call to a method in which
+	// no parameters are eliminated, e.g.
+	// 	new(T).f(g())
+	// where
+	//  	func (recv *T) f(x, y int) { body }
+	//  	func g() (int, int)
+	// This would be literalized to:
+	// 	func (recv *T, x, y int) { body }(new(T), g()),
+	// which is not a valid argument list because g() must appear alone.
+	// Reject this case for now.
+	if len(args) == 2 && args[0] != nil && args[1] != nil && is[*types.Tuple](args[1].typ) {
+		return nil, fmt.Errorf("can't yet inline spread call to method")
+	}
+
+	// Infallible general case: literalization.
+	//
+	//    func(params) { body }(args)
+	//
+	logf("strategy: literalization")
+	funcLit := &ast.FuncLit{
+		Type: calleeDecl.Type,
+		Body: calleeDecl.Body,
+	}
+	// clear positions before prepending the binding decl below, since the
+	// binding decl contains syntax from the caller and we must not mutate the
+	// caller. (This was a prior bug.)
+	clearPositions(funcLit)
+
+	// Literalization can still make use of a binding
+	// decl as it gives a more natural reading order:
+	//
+	//    func() { var params = args; body }()
+	//
+	// TODO(adonovan): relax the allResultsUnreferenced requirement
+	// by adding a parameter-only (no named results) binding decl.
+	if bindingDecl != nil && allResultsUnreferenced {
+		funcLit.Type.Params.List = nil
+		remainingArgs = nil
+		res.bindingDecl = true
+		funcLit.Body.List = prepend(bindingDecl.stmt, funcLit.Body.List...)
+	}
+
+	// Emit a new call to a function literal in place of
+	// the callee name, with appropriate replacements.
+	newCall := &ast.CallExpr{
+		Fun:      funcLit,
+		Ellipsis: token.NoPos, // f(slice...) is always simplified
+		Args:     remainingArgs,
+	}
+	res.old = caller.Call
+	res.new = newCall
+	return res, nil
+}
+
+// renameFreeObjs computes the renaming of the callee's free identifiers.
+// It returns a slice of names (identifiers or selector expressions) corresponding
+// to the callee's free objects (gobCallee.FreeObjs).
+func (st *state) renameFreeObjs(istate *importState) ([]ast.Expr, error) {
+	caller, callee := st.caller, &st.callee.impl
+	objRenames := make([]ast.Expr, len(callee.FreeObjs)) // nil => no change
+	for i, obj := range callee.FreeObjs {
+		// obj is a free object of the callee.
+		//
+		// Possible cases are:
+		// - builtin function, type, or value (e.g. nil, zero)
+		//   => check not shadowed in caller.
+		// - package-level var/func/const/types
+		//   => same package: check not shadowed in caller.
+		//   => otherwise: import other package, form a qualified identifier.
+		//      (Unexported cross-package references were rejected already.)
+		// - type parameter
+		//   => not yet supported
+		// - pkgname
+		//   => import other package and use its local name.
+		//
+		// There can be no free references to labels, fields, or methods.
+
+		// Note that we must consider potential shadowing both
+		// at the caller side (caller.lookup) and, when
+		// choosing new PkgNames, within the callee (obj.shadow).
+
+		var newName ast.Expr
+		if obj.Kind == "pkgname" {
+			// Use locally appropriate import, creating as needed.
+			n := istate.localName(obj.PkgPath, obj.PkgName, obj.Name, obj.Shadow)
+			newName = makeIdent(n) // imported package
+		} else if !obj.ValidPos {
+			// Built-in function, type, or value (e.g. nil, zero):
+			// check not shadowed at caller.
+			found := caller.lookup(obj.Name) // always finds something
+			if found.Pos().IsValid() {
+				return nil, fmt.Errorf("cannot inline, because the callee refers to built-in %q, which in the caller is shadowed by a %s (declared at line %d)",
+					obj.Name, objectKind(found),
+					caller.Fset.PositionFor(found.Pos(), false).Line)
+			}
+
+		} else {
+			// Must be reference to package-level var/func/const/type,
+			// since type parameters are not yet supported.
+			qualify := false
+			if obj.PkgPath == callee.PkgPath {
+				// reference within callee package
+				if caller.Types.Path() == callee.PkgPath {
+					// Caller and callee are in same package.
+					// Check caller has not shadowed the decl.
+					//
+					// This may fail if the callee is "fake", such as for signature
+					// refactoring where the callee is modified to be a trivial wrapper
+					// around the refactored signature.
+					found := caller.lookup(obj.Name)
+					if found != nil && !isPkgLevel(found) {
+						return nil, fmt.Errorf("cannot inline, because the callee refers to %s %q, which in the caller is shadowed by a %s (declared at line %d)",
+							obj.Kind, obj.Name,
+							objectKind(found),
+							caller.Fset.PositionFor(found.Pos(), false).Line)
+					}
+				} else {
+					// Cross-package reference.
+					qualify = true
+				}
+			} else {
+				// Reference to a package-level declaration
+				// in another package, without a qualified identifier:
+				// it must be a dot import.
+				qualify = true
+			}
+
+			// Form a qualified identifier, pkg.Name.
+			if qualify {
+				pkgName := istate.localName(obj.PkgPath, obj.PkgName, obj.PkgName, obj.Shadow)
+				newName = &ast.SelectorExpr{
+					X:   makeIdent(pkgName),
+					Sel: makeIdent(obj.Name),
+				}
+			}
+		}
+		objRenames[i] = newName
+	}
+	return objRenames, nil
+}
+
+type argument struct {
+	expr          ast.Expr
+	typ           types.Type      // may be tuple for sole non-receiver arg in spread call
+	constant      constant.Value  // value of argument if constant
+	spread        bool            // final arg is call() assigned to multiple params
+	pure          bool            // expr is pure (doesn't read variables)
+	effects       bool            // expr has effects (updates variables)
+	duplicable    bool            // expr may be duplicated
+	freevars      map[string]bool // free names of expr
+	variadic      bool            // is explicit []T{...} for eliminated variadic
+	desugaredRecv bool            // is *recv or &recv, where operator was elided
+}
+
+// typeArguments returns the type arguments of the call.
+// It only collects the arguments that are explicitly provided; it does
+// not attempt type inference.
+func (st *state) typeArguments(call *ast.CallExpr) []*argument {
+	var exprs []ast.Expr
+	switch d := ast.Unparen(call.Fun).(type) {
+	case *ast.IndexExpr:
+		exprs = []ast.Expr{d.Index}
+	case *ast.IndexListExpr:
+		exprs = d.Indices
+	default:
+		// No type  arguments
+		return nil
+	}
+	var args []*argument
+	for _, e := range exprs {
+		arg := &argument{expr: e, freevars: freeVars(st.caller.Info, e)}
+		args = append(args, arg)
+	}
+	return args
+}
+
+// arguments returns the effective arguments of the call.
+//
+// If the receiver argument and parameter have
+// different pointerness, make the "&" or "*" explicit.
+//
+// Also, if x.f() is shorthand for promoted method x.y.f(),
+// make the .y explicit in T.f(x.y, ...).
+//
+// Beware that:
+//
+//   - a method can only be called through a selection, but only
+//     the first of these two forms needs special treatment:
+//
+//     expr.f(args)     -> ([&*]expr, args)	MethodVal
+//     T.f(recv, args)  -> (    expr, args)	MethodExpr
+//
+//   - the presence of a value in receiver-position in the call
+//     is a property of the caller, not the callee. A method
+//     (calleeDecl.Recv != nil) may be called like an ordinary
+//     function.
+//
+//   - the types.Signatures seen by the caller (from
+//     StaticCallee) and by the callee (from decl type)
+//     differ in this case.
+//
+// In a spread call f(g()), the sole ordinary argument g(),
+// always last in args, has a tuple type.
+//
+// We compute type-based predicates like pure, duplicable,
+// freevars, etc, now, before we start modifying syntax.
+func (st *state) arguments(caller *Caller, calleeDecl *ast.FuncDecl, assign1 func(*types.Var) bool) ([]*argument, error) {
+	var args []*argument
+
+	callArgs := caller.Call.Args
+	if calleeDecl.Recv != nil {
+		if len(st.callee.impl.TypeParams) > 0 {
+			return nil, fmt.Errorf("cannot inline: generic methods not yet supported")
+		}
+		sel := ast.Unparen(caller.Call.Fun).(*ast.SelectorExpr)
+		seln := caller.Info.Selections[sel]
+		var recvArg ast.Expr
+		switch seln.Kind() {
+		case types.MethodVal: // recv.f(callArgs)
+			recvArg = sel.X
+		case types.MethodExpr: // T.f(recv, callArgs)
+			recvArg = callArgs[0]
+			callArgs = callArgs[1:]
+		}
+		if recvArg != nil {
+			// Compute all the type-based predicates now,
+			// before we start meddling with the syntax;
+			// the meddling will update them.
+			arg := &argument{
+				expr:       recvArg,
+				typ:        caller.Info.TypeOf(recvArg),
+				constant:   caller.Info.Types[recvArg].Value,
+				pure:       pure(caller.Info, assign1, recvArg),
+				effects:    st.effects(caller.Info, recvArg),
+				duplicable: duplicable(caller.Info, recvArg),
+				freevars:   freeVars(caller.Info, recvArg),
+			}
+			recvArg = nil // prevent accidental use
+
+			// Move receiver argument recv.f(args) to argument list f(&recv, args).
+			args = append(args, arg)
+
+			// Make field selections explicit (recv.f -> recv.y.f),
+			// updating arg.{expr,typ}.
+			indices := seln.Index()
+			for _, index := range indices[:len(indices)-1] {
+				fld := typeparams.CoreType(typeparams.Deref(arg.typ)).(*types.Struct).Field(index)
+				if fld.Pkg() != caller.Types && !fld.Exported() {
+					return nil, fmt.Errorf("in %s, implicit reference to unexported field .%s cannot be made explicit",
+						debugFormatNode(caller.Fset, caller.Call.Fun),
+						fld.Name())
+				}
+				if isPointer(arg.typ) {
+					arg.pure = false // implicit *ptr operation => impure
+				}
+				arg.expr = &ast.SelectorExpr{
+					X:   arg.expr,
+					Sel: makeIdent(fld.Name()),
+				}
+				arg.typ = fld.Type()
+				arg.duplicable = false
+			}
+
+			// Make * or & explicit.
+			argIsPtr := isPointer(arg.typ)
+			paramIsPtr := isPointer(seln.Obj().Type().Underlying().(*types.Signature).Recv().Type())
+			if !argIsPtr && paramIsPtr {
+				// &recv
+				arg.expr = &ast.UnaryExpr{Op: token.AND, X: arg.expr}
+				arg.typ = types.NewPointer(arg.typ)
+				arg.desugaredRecv = true
+			} else if argIsPtr && !paramIsPtr {
+				// *recv
+				arg.expr = &ast.StarExpr{X: arg.expr}
+				arg.typ = typeparams.Deref(arg.typ)
+				arg.duplicable = false
+				arg.pure = false
+				arg.desugaredRecv = true
+			}
+		}
+	}
+	for _, expr := range callArgs {
+		tv := caller.Info.Types[expr]
+		args = append(args, &argument{
+			expr:       expr,
+			typ:        tv.Type,
+			constant:   tv.Value,
+			spread:     is[*types.Tuple](tv.Type), // => last
+			pure:       pure(caller.Info, assign1, expr),
+			effects:    st.effects(caller.Info, expr),
+			duplicable: duplicable(caller.Info, expr),
+			freevars:   freeVars(caller.Info, expr),
+		})
+	}
+
+	// Re-typecheck each constant argument expression in a neutral context.
+	//
+	// In a call such as func(int16){}(1), the type checker infers
+	// the type "int16", not "untyped int", for the argument 1,
+	// because it has incorporated information from the left-hand
+	// side of the assignment implicit in parameter passing, but
+	// of course in a different context, the expression 1 may have
+	// a different type.
+	//
+	// So, we must use CheckExpr to recompute the type of the
+	// argument in a neutral context to find its inherent type.
+	// (This is arguably a bug in go/types, but I'm pretty certain
+	// I requested it be this way long ago... -adonovan)
+	//
+	// This is only needed for constants. Other implicit
+	// assignment conversions, such as unnamed-to-named struct or
+	// chan to <-chan, do not result in the type-checker imposing
+	// the LHS type on the RHS value.
+	for _, arg := range args {
+		if arg.constant == nil {
+			continue
+		}
+		info := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
+		if err := types.CheckExpr(caller.Fset, caller.Types, caller.Call.Pos(), arg.expr, info); err != nil {
+			return nil, err
+		}
+		arg.typ = info.TypeOf(arg.expr)
+	}
+
+	return args, nil
+}
+
+type parameter struct {
+	obj       *types.Var // parameter var from caller's signature
+	fieldType ast.Expr   // syntax of type, from calleeDecl.Type.{Recv,Params}
+	info      *paramInfo // information from AnalyzeCallee
+	variadic  bool       // (final) parameter is unsimplified ...T
+}
+
+// A replacer replaces an identifier at the given offset in the callee.
+// The replacement tree must not belong to the caller; use cloneNode as needed.
+// If unpackVariadic is set, the replacement is a composite resulting from
+// variadic elimination, and may be unpacked into variadic calls.
+type replacer = func(offset int, repl ast.Expr, unpackVariadic bool)
+
+// substituteTypeParams replaces type parameters in the callee with the
+// corresponding type arguments from the call.
+func substituteTypeParams(logf logger, typeParams []*paramInfo, typeArgs []*argument, replace replacer) error {
+	assert(len(typeParams) == len(typeArgs), "mismatched number of type params/args")
+	for i, paramInfo := range typeParams {
+		arg := typeArgs[i]
+		// Perform a simplified, conservative shadow analysis: fail if there is any shadowing.
+		for free := range arg.freevars {
+			if paramInfo.Shadow[free] != 0 {
+				return fmt.Errorf("cannot inline: type argument #%d (type parameter %s) is shadowed", i, paramInfo.Name)
+			}
+		}
+		logf("replacing type param %s with %s", paramInfo.Name, debugFormatNode(token.NewFileSet(), arg.expr))
+		for _, ref := range paramInfo.Refs {
+			replace(ref.Offset, internalastutil.CloneNode(arg.expr), false)
+		}
+	}
+	return nil
+}
+
+// syncParamFieldTypes synchronizes the fieldType of each parameter in params
+// with the mutated calleeDecl AST. This is necessary because substituteTypeParams
+// mutates the calleeDecl AST, replacing type nodes, but params still references
+// the original (now outdated) type nodes.
+func syncParamFieldTypes(calleeDecl *ast.FuncDecl, params []*parameter) {
+	var i int
+	setFieldType := func(t ast.Expr) {
+		assert(i < len(params), "mismatched parameter count")
+		params[i].fieldType = t
+		i++
+	}
+
+	if calleeDecl.Recv != nil && len(calleeDecl.Recv.List) > 0 {
+		setFieldType(calleeDecl.Recv.List[0].Type)
+	}
+	if calleeDecl.Type.Params != nil {
+		for _, field := range calleeDecl.Type.Params.List {
+			if field.Names == nil {
+				setFieldType(field.Type)
+			} else {
+				for range field.Names {
+					setFieldType(field.Type)
+				}
+			}
+		}
+	}
+	assert(i == len(params), "mismatched parameter count")
+}
+
+// substitute implements parameter elimination by substitution.
+//
+// It considers each parameter and its corresponding argument in turn
+// and evaluate these conditions:
+//
+//   - the parameter is neither address-taken nor assigned;
+//   - the argument is pure;
+//   - if the parameter refcount is zero, the argument must
+//     not contain the last use of a local var;
+//   - if the parameter refcount is > 1, the argument must be duplicable;
+//   - the argument (or types.Default(argument) if it's untyped) has
+//     the same type as the parameter.
+//
+// If all conditions are met then the parameter can be substituted and
+// each reference to it replaced by the argument. In that case, the
+// replaceCalleeID function is called for each reference to the
+// parameter, and is provided with its relative offset and replacement
+// expression (argument), and the corresponding elements of params and
+// args are replaced by nil.
+func substitute(logf logger, caller *Caller, params []*parameter, args []*argument, effects []int, falcon falconResult, replace replacer) {
+	// Inv:
+	//  in        calls to     variadic, len(args) >= len(params)-1
+	//  in spread calls to non-variadic, len(args) <  len(params)
+	//  in spread calls to     variadic, len(args) <= len(params)
+	// (In spread calls len(args) = 1, or 2 if call has receiver.)
+	// Non-spread variadics have been simplified away already,
+	// so the args[i] lookup is safe if we stop after the spread arg.
+	assert(len(args) <= len(params), "too many arguments")
+
+	// Collect candidates for substitution.
+	//
+	// An argument is a candidate if it is not otherwise rejected, and any free
+	// variables that are shadowed only by other parameters.
+	//
+	// Therefore, substitution candidates are represented by a graph, where edges
+	// lead from each argument to the other arguments that, if substituted, would
+	// allow the argument to be substituted. We collect these edges in the
+	// [substGraph]. Any node that is known not to be elided from the graph.
+	// Arguments in this graph with no edges are substitutable independent of
+	// other nodes, though they may be removed due to falcon or effects analysis.
+	sg := make(substGraph)
+next:
+	for i, param := range params {
+		arg := args[i]
+
+		// Check argument against parameter.
+		//
+		// Beware: don't use types.Info on arg since
+		// the syntax may be synthetic (not created by parser)
+		// and thus lacking positions and types;
+		// do it earlier (see pure/duplicable/freevars).
+
+		if arg.spread {
+			// spread => last argument, but not always last parameter
+			logf("keeping param %q and following ones: argument %s is spread",
+				param.info.Name, debugFormatNode(caller.Fset, arg.expr))
+			return // give up
+		}
+		assert(!param.variadic, "unsimplified variadic parameter")
+		if param.info.Escapes {
+			logf("keeping param %q: escapes from callee", param.info.Name)
+			continue
+		}
+		if param.info.Assigned {
+			logf("keeping param %q: assigned by callee", param.info.Name)
+			continue // callee needs the parameter variable
+		}
+		if len(param.info.Refs) > 1 && !arg.duplicable {
+			logf("keeping param %q: argument is not duplicable", param.info.Name)
+			continue // incorrect or poor style to duplicate an expression
+		}
+		if len(param.info.Refs) == 0 {
+			if arg.effects {
+				logf("keeping param %q: though unreferenced, it has effects", param.info.Name)
+				continue
+			}
+
+			// If the caller is within a function body,
+			// eliminating an unreferenced parameter might
+			// remove the last reference to a caller local var.
+			if caller.enclosingFunc != nil {
+				for free := range arg.freevars {
+					// TODO(rfindley): we can get this 100% right by looking for
+					// references among other arguments which have non-zero references
+					// within the callee.
+					if v, ok := caller.lookup(free).(*types.Var); ok && within(v.Pos(), caller.enclosingFunc.Body) && !isUsedOutsideCall(caller, v) {
+
+						// Check to see if the substituted var is used within other args
+						// whose corresponding params ARE used in the callee
+						usedElsewhere := func() bool {
+							for i, param := range params {
+								if i < len(args) && len(param.info.Refs) > 0 { // excludes original param
+									for name := range args[i].freevars {
+										if caller.lookup(name) == v {
+											return true
+										}
+									}
+								}
+							}
+							return false
+						}
+						if !usedElsewhere() {
+							logf("keeping param %q: arg contains perhaps the last reference to caller local %v @ %v",
+								param.info.Name, v, caller.Fset.PositionFor(v.Pos(), false))
+							continue next
+						}
+					}
+				}
+			}
+		}
+
+		// Arg is a potential substitution candidate: analyze its shadowing.
+		//
+		// Consider inlining a call f(z, 1) to
+		//
+		// 	func f(x, y int) int { z := y; return x + y + z }
+		//
+		// we can't replace x in the body by z (or any
+		// expression that has z as a free identifier) because there's an
+		// intervening declaration of z that would shadow the caller's one.
+		//
+		// However, we *could* replace x in the body by y, as long as the y
+		// parameter is also removed by substitution.
+
+		sg[arg] = nil // Absent shadowing, the arg is substitutable.
+		for free := range arg.freevars {
+			switch s := param.info.Shadow[free]; {
+			case s < 0:
+				// Shadowed by a non-parameter symbol, so arg is not substitutable.
+				delete(sg, arg)
+			case s > 0:
+				// Shadowed by a parameter; arg may be substitutable, if only shadowed
+				// by other substitutable parameters.
+				if s > len(args) {
+					// Defensive: this should not happen in the current factoring, since
+					// spread arguments are already handled.
+					delete(sg, arg)
+				}
+				if edges, ok := sg[arg]; ok {
+					sg[arg] = append(edges, args[s-1])
+				}
+			}
+		}
+	}
+
+	// Process the initial state of the substitution graph.
+	sg.prune()
+
+	// Now we check various conditions on the substituted argument set as a
+	// whole. These conditions reject substitution candidates, but since their
+	// analysis depends on the full set of candidates, we do not process side
+	// effects of their candidate rejection until after the analysis completes,
+	// in a call to prune. After pruning, we must re-run the analysis to check
+	// for additional rejections.
+	//
+	// Here's an example of that in practice:
+	//
+	// 	var a [3]int
+	//
+	// 	func falcon(x, y, z int) {
+	// 		_ = x + a[y+z]
+	// 	}
+	//
+	// 	func _() {
+	// 		var y int
+	// 		const x, z = 1, 2
+	// 		falcon(y, x, z)
+	// 	}
+	//
+	// In this example, arguments 0 and 1 are shadowed by each other's
+	// corresponding parameter, and so each can be substituted only if they are
+	// both substituted. But the fallible constant analysis finds a violated
+	// constraint: x + z = 3, and so the constant array index would cause a
+	// compile-time error if argument 1 (x) were substituted. Therefore,
+	// following the falcon analysis, we must also prune argument 0.
+	//
+	// As far as I (rfindley) can tell, the falcon analysis should always succeed
+	// after the first pass, as it's not possible for additional bindings to
+	// cause new constraint failures. Nevertheless, we re-run it to be sure.
+	//
+	// However, the same cannot be said of the effects analysis, as demonstrated
+	// by this example:
+	//
+	// 	func effects(w, x, y, z int) {
+	// 		_ = x + w + y + z
+	// 	}
+
+	// 	func _() {
+	// 		v := 0
+	// 		w := func() int { v++; return 0 }
+	// 		x := func() int { v++; return 0 }
+	// 		y := func() int { v++; return 0 }
+	// 		effects(x(), w(), y(), x()) //@ inline(re"effects", effects)
+	// 	}
+	//
+	// In this example, arguments 0, 1, and 3 are related by the substitution
+	// graph. The first effects analysis implies that arguments 0 and 1 must be
+	// bound, and therefore argument 3 must be bound. But then a subsequent
+	// effects analysis forces argument 2 to also be bound.
+
+	// Reject constant arguments as substitution candidates if they cause
+	// violation of falcon constraints.
+	//
+	// Keep redoing the analysis until we no longer reject additional arguments,
+	// as the set of substituted parameters affects the falcon package.
+	for checkFalconConstraints(logf, params, args, falcon, sg) {
+		sg.prune()
+	}
+
+	// As a final step, introduce bindings to resolve any
+	// evaluation order hazards. This must be done last, as
+	// additional subsequent bindings could introduce new hazards.
+	//
+	// As with the falcon analysis, keep redoing the analysis until the no more
+	// arguments are rejected.
+	for resolveEffects(logf, args, effects, sg) {
+		sg.prune()
+	}
+
+	// The remaining candidates are safe to substitute.
+	for i, param := range params {
+		if arg := args[i]; sg.has(arg) {
+
+			// It is safe to substitute param and replace it with arg.
+			// The formatter introduces parens as needed for precedence.
+			//
+			// Because arg.expr belongs to the caller,
+			// we clone it before splicing it into the callee tree.
+			logf("replacing parameter %q by argument %q",
+				param.info.Name, debugFormatNode(caller.Fset, arg.expr))
+			for _, ref := range param.info.Refs {
+				// Apply any transformations necessary for this reference.
+				argExpr := arg.expr
+
+				// If the reference itself is being selected, and we applied desugaring
+				// (an explicit &x or *x), we can undo that desugaring here as it is
+				// not necessary for a selector. We don't need to check addressability
+				// here because if we desugared, the receiver must have been
+				// addressable.
+				if ref.IsSelectionOperand && arg.desugaredRecv {
+					switch e := argExpr.(type) {
+					case *ast.UnaryExpr:
+						argExpr = e.X
+					case *ast.StarExpr:
+						argExpr = e.X
+					}
+				}
+
+				// If the reference requires exact type agreement between parameter and
+				// argument, wrap the argument in an explicit conversion if
+				// substitution might materially change its type. (We already did the
+				// necessary shadowing check on the parameter type syntax.)
+				//
+				// The types must agree in any of these cases:
+				// - the argument affects type inference;
+				// - the reference's concrete type is assigned to an interface type;
+				// - the reference is not an assignment, nor a trivial conversion of an untyped constant.
+				//
+				// In all other cases, no explicit conversion is necessary as either
+				// the type does not matter, or must have already agreed for well-typed
+				// code.
+				//
+				// This is only needed for substituted arguments. All other arguments
+				// are given explicit types in either a binding decl or when using the
+				// literalization strategy.
+				//
+				// If the types are identical, we can eliminate
+				// redundant type conversions such as this:
+				//
+				// Callee:
+				//    func f(i int32) { fmt.Println(i) }
+				// Caller:
+				//    func g() { f(int32(1)) }
+				// Inlined as:
+				//    func g() { fmt.Println(int32(int32(1)))
+				//
+				// Recall that non-trivial does not imply non-identical for constant
+				// conversions; however, at this point state.arguments has already
+				// re-typechecked the constant and set arg.type to its (possibly
+				// "untyped") inherent type, so the conversion from untyped 1 to int32
+				// is non-trivial even though both arg and param have identical types
+				// (int32).
+				needType := ref.AffectsInference ||
+					(ref.Assignable && ref.IfaceAssignment && !param.info.IsInterface) ||
+					(!ref.Assignable && !trivialConversion(arg.constant, arg.typ, param.obj.Type()))
+
+				if needType &&
+					!types.Identical(types.Default(arg.typ), param.obj.Type()) {
+
+					// If arg.expr is already an interface call, strip it.
+					if call, ok := argExpr.(*ast.CallExpr); ok && len(call.Args) == 1 {
+						if typ, ok := isConversion(caller.Info, call); ok && isNonTypeParamInterface(typ) {
+							argExpr = call.Args[0]
+						}
+					}
+
+					argExpr = convert(param.fieldType, argExpr)
+					logf("param %q (offset %d): adding explicit %s -> %s conversion around argument",
+						param.info.Name, ref.Offset, arg.typ, param.obj.Type())
+				}
+				replace(ref.Offset, internalastutil.CloneNode(argExpr).(ast.Expr), arg.variadic)
+			}
+			params[i] = nil // substituted
+			args[i] = nil   // substituted
+		}
+	}
+}
+
+// isConversion reports whether the given call is a type conversion, returning
+// (operand, true) if so.
+//
+// If the call is not a conversion, it returns (nil, false).
+func isConversion(info *types.Info, call *ast.CallExpr) (types.Type, bool) {
+	if tv, ok := info.Types[call.Fun]; ok && tv.IsType() {
+		return tv.Type, true
+	}
+	return nil, false
+}
+
+// isNonTypeParamInterface reports whether t is a non-type parameter interface
+// type.
+func isNonTypeParamInterface(t types.Type) bool {
+	return !typeparams.IsTypeParam(t) && types.IsInterface(t)
+}
+
+// isUsedOutsideCall reports whether v is used outside of caller.Call, within
+// the body of caller.enclosingFunc.
+func isUsedOutsideCall(caller *Caller, v *types.Var) bool {
+	used := false
+	ast.Inspect(caller.enclosingFunc.Body, func(n ast.Node) bool {
+		if n == caller.Call {
+			return false
+		}
+		switch n := n.(type) {
+		case *ast.Ident:
+			if use := caller.Info.Uses[n]; use == v {
+				used = true
+			}
+		case *ast.FuncType:
+			// All params are used.
+			for _, fld := range n.Params.List {
+				for _, n := range fld.Names {
+					if def := caller.Info.Defs[n]; def == v {
+						used = true
+					}
+				}
+			}
+		}
+		return !used // keep going until we find a use
+	})
+	return used
+}
+
+// checkFalconConstraints checks whether constant arguments
+// are safe to substitute (e.g. s[i] -> ""[0] is not safe.)
+//
+// Any failed constraint causes us to reject all constant arguments as
+// substitution candidates (by clearing args[i].substitution=false).
+//
+// TODO(adonovan): we could obtain a finer result rejecting only the
+// freevars of each failed constraint, and processing constraints in
+// order of increasing arity, but failures are quite rare.
+func checkFalconConstraints(logf logger, params []*parameter, args []*argument, falcon falconResult, sg substGraph) bool {
+	// Create a dummy package, as this is the only
+	// way to create an environment for CheckExpr.
+	pkg := types.NewPackage("falcon", "falcon")
+
+	// Declare types used by constraints.
+	for _, typ := range falcon.Types {
+		logf("falcon env: type %s %s", typ.Name, types.Typ[typ.Kind])
+		pkg.Scope().Insert(types.NewTypeName(token.NoPos, pkg, typ.Name, types.Typ[typ.Kind]))
+	}
+
+	// Declared constants and variables for parameters.
+	nconst := 0
+	for i, param := range params {
+		name := param.info.Name
+		if name == "" {
+			continue // unreferenced
+		}
+		arg := args[i]
+		if arg.constant != nil && sg.has(arg) && param.info.FalconType != "" {
+			t := pkg.Scope().Lookup(param.info.FalconType).Type()
+			pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, t, arg.constant))
+			logf("falcon env: const %s %s = %v", name, param.info.FalconType, arg.constant)
+			nconst++
+		} else {
+			v := types.NewVar(token.NoPos, pkg, name, arg.typ)
+			typesinternal.SetVarKind(v, typesinternal.PackageVar)
+			pkg.Scope().Insert(v)
+			logf("falcon env: var %s %s", name, arg.typ)
+		}
+	}
+	if nconst == 0 {
+		return false // nothing to do
+	}
+
+	// Parse and evaluate the constraints in the environment.
+	fset := token.NewFileSet()
+	removed := false
+	for _, falcon := range falcon.Constraints {
+		expr, err := parser.ParseExprFrom(fset, "falcon", falcon, 0)
+		if err != nil {
+			panic(fmt.Sprintf("failed to parse falcon constraint %s: %v", falcon, err))
+		}
+		if err := types.CheckExpr(fset, pkg, token.NoPos, expr, nil); err != nil {
+			logf("falcon: constraint %s violated: %v", falcon, err)
+			for j, arg := range args {
+				if arg.constant != nil && sg.has(arg) {
+					logf("keeping param %q due falcon violation", params[j].info.Name)
+					removed = sg.remove(arg) || removed
+				}
+			}
+			break
+		}
+		logf("falcon: constraint %s satisfied", falcon)
+	}
+	return removed
+}
+
+// resolveEffects marks arguments as non-substitutable to resolve
+// hazards resulting from the callee evaluation order described by the
+// effects list.
+//
+// To do this, each argument is categorized as a read (R), write (W),
+// or pure. A hazard occurs when the order of evaluation of a W
+// changes with respect to any R or W. Pure arguments can be
+// effectively ignored, as they can be safely evaluated in any order.
+//
+// The callee effects list contains the index of each parameter in the
+// order it is first evaluated during execution of the callee. In
+// addition, the two special values R∞ and W∞ indicate the relative
+// position of the callee's first non-parameter read and its first
+// effects (or other unknown behavior).
+// For example, the list [0 2 1 R∞ 3 W∞] for func(a, b, c, d)
+// indicates that the callee referenced parameters a, c, and b,
+// followed by an arbitrary read, then parameter d, and finally
+// unknown behavior.
+//
+// When an argument is marked as not substitutable, we say that it is
+// 'bound', in the sense that its evaluation occurs in a binding decl
+// or literalized call. Such bindings always occur in the original
+// callee parameter order.
+//
+// In this context, "resolving hazards" means binding arguments so
+// that they are evaluated in a valid, hazard-free order. A trivial
+// solution to this problem would be to bind all arguments, but of
+// course that's not useful. The goal is to bind as few arguments as
+// possible.
+//
+// The algorithm proceeds by inspecting arguments in reverse parameter
+// order (right to left), preserving the invariant that every
+// higher-ordered argument is either already substituted or does not
+// need to be substituted. At each iteration, if there is an
+// evaluation hazard in the callee effects relative to the current
+// argument, the argument must be bound. Subsequently, if the argument
+// is bound for any reason, each lower-ordered argument must also be
+// bound if either the argument or lower-order argument is a
+// W---otherwise the binding itself would introduce a hazard.
+//
+// Thus, after each iteration, there are no hazards relative to the
+// current argument. Subsequent iterations cannot introduce hazards
+// with that argument because they can result only in additional
+// binding of lower-ordered arguments.
+func resolveEffects(logf logger, args []*argument, effects []int, sg substGraph) bool {
+	effectStr := func(effects bool, idx int) string {
+		i := fmt.Sprint(idx)
+		if idx == len(args) {
+			i = "∞"
+		}
+		return string("RW"[btoi(effects)]) + i
+	}
+	removed := false
+	for i, argi := range slices.Backward(args) {
+		if sg.has(argi) && !argi.pure {
+			// i is not bound: check whether it must be bound due to hazards.
+			idx := slices.Index(effects, i)
+			if idx >= 0 {
+				for _, j := range effects[:idx] {
+					var (
+						ji int  // effective param index
+						jw bool // j is a write
+					)
+					if j == winf || j == rinf {
+						jw = j == winf
+						ji = len(args)
+					} else {
+						jw = args[j].effects
+						ji = j
+					}
+					if ji > i && (jw || argi.effects) { // out of order evaluation
+						logf("binding argument %s: preceded by %s",
+							effectStr(argi.effects, i), effectStr(jw, ji))
+
+						removed = sg.remove(argi) || removed
+						break
+					}
+				}
+			}
+		}
+		if !sg.has(argi) {
+			for j := range i {
+				argj := args[j]
+				if argj.pure {
+					continue
+				}
+				if (argi.effects || argj.effects) && sg.has(argj) {
+					logf("binding argument %s: %s is bound",
+						effectStr(argj.effects, j), effectStr(argi.effects, i))
+
+					removed = sg.remove(argj) || removed
+				}
+			}
+		}
+	}
+	return removed
+}
+
+// A substGraph is a directed graph representing arguments that may be
+// substituted, provided all of their related arguments (or "dependencies") are
+// also substituted. The candidates arguments for substitution are the keys in
+// this graph, and the edges represent shadowing of free variables of the key
+// by parameters corresponding to the dependency arguments.
+//
+// Any argument not present as a map key is known not to be substitutable. Some
+// arguments may have edges leading to other arguments that are not present in
+// the graph. In this case, those arguments also cannot be substituted, because
+// they have free variables that are shadowed by parameters that cannot be
+// substituted. Calling [substGraph.prune] removes these arguments from the
+// graph.
+//
+// The 'prune' operation is not built into the 'remove' step both because
+// analyses (falcon, effects) need local information about each argument
+// independent of dependencies, and for the efficiency of pruning once en masse
+// after each analysis.
+type substGraph map[*argument][]*argument
+
+// has reports whether arg is a candidate for substitution.
+func (g substGraph) has(arg *argument) bool {
+	_, ok := g[arg]
+	return ok
+}
+
+// remove marks arg as not substitutable, reporting whether the arg was
+// previously substitutable.
+//
+// remove does not have side effects on other arguments that may be
+// unsubstitutable as a result of their dependency being removed.
+// Call [substGraph.prune] to propagate these side effects, removing dependent
+// arguments.
+func (g substGraph) remove(arg *argument) bool {
+	pre := len(g)
+	delete(g, arg)
+	return len(g) < pre
+}
+
+// prune updates the graph to remove any keys that reach other arguments not
+// present in the graph.
+func (g substGraph) prune() {
+	// visit visits the forward transitive closure of arg and reports whether any
+	// missing argument was encountered, removing all nodes on the path to it
+	// from arg.
+	//
+	// The seen map is used for cycle breaking. In the presence of cycles, visit
+	// may report a false positive for an intermediate argument. For example,
+	// consider the following graph, where only a and b are candidates for
+	// substitution (meaning, only a and b are present in the graph).
+	//
+	//   a ↔ b
+	//   ↓
+	//  [c]
+	//
+	// In this case, starting a visit from a, visit(b, seen) may report 'true',
+	// because c has not yet been considered. For this reason, we must guarantee
+	// that visit is called with an empty seen map at least once for each node.
+	var visit func(*argument, map[*argument]unit) bool
+	visit = func(arg *argument, seen map[*argument]unit) bool {
+		deps, ok := g[arg]
+		if !ok {
+			return false
+		}
+		if _, ok := seen[arg]; !ok {
+			seen[arg] = unit{}
+			for _, dep := range deps {
+				if !visit(dep, seen) {
+					delete(g, arg)
+					return false
+				}
+			}
+		}
+		return true
+	}
+	for arg := range g {
+		// Remove any argument that is, or transitively depends upon,
+		// an unsubstitutable argument.
+		//
+		// Each visitation gets a fresh cycle-breaking set.
+		visit(arg, make(map[*argument]unit))
+	}
+}
+
+// updateCalleeParams updates the calleeDecl syntax to remove
+// substituted parameters and move the receiver (if any) to the head
+// of the ordinary parameters.
+func updateCalleeParams(calleeDecl *ast.FuncDecl, params []*parameter) {
+	// The logic is fiddly because of the three forms of ast.Field:
+	//
+	//	func(int), func(x int), func(x, y int)
+	//
+	// Also, ensure that all remaining parameters are named
+	// to avoid a mix of named/unnamed when joining (recv, params...).
+	// func (T) f(int, bool) -> (_ T, _ int, _ bool)
+	// (Strictly, we need do this only for methods and only when
+	// the namednesses of Recv and Params differ; that might be tidier.)
+
+	paramIdx := 0 // index in original parameter list (incl. receiver)
+	var newParams []*ast.Field
+	filterParams := func(field *ast.Field) {
+		var names []*ast.Ident
+		if field.Names == nil {
+			// Unnamed parameter field (e.g. func f(int)
+			if params[paramIdx] != nil {
+				// Give it an explicit name "_" since we will
+				// make the receiver (if any) a regular parameter
+				// and one cannot mix named and unnamed parameters.
+				names = append(names, makeIdent("_"))
+			}
+			paramIdx++
+		} else {
+			// Named parameter field e.g. func f(x, y int)
+			// Remove substituted parameters in place.
+			// If all were substituted, delete field.
+			for _, id := range field.Names {
+				if pinfo := params[paramIdx]; pinfo != nil {
+					// Rename unreferenced parameters with "_".
+					// This is crucial for binding decls, since
+					// unlike parameters, they are subject to
+					// "unreferenced var" checks.
+					if len(pinfo.info.Refs) == 0 {
+						id = makeIdent("_")
+					}
+					names = append(names, id)
+				}
+				paramIdx++
+			}
+		}
+		if names != nil {
+			newParams = append(newParams, &ast.Field{
+				Names: names,
+				Type:  field.Type,
+			})
+		}
+	}
+	if calleeDecl.Recv != nil {
+		filterParams(calleeDecl.Recv.List[0])
+		calleeDecl.Recv = nil
+	}
+	for _, field := range calleeDecl.Type.Params.List {
+		filterParams(field)
+	}
+	calleeDecl.Type.Params.List = newParams
+}
+
+// bindingDeclInfo records information about the binding decl produced by
+// createBindingDecl.
+type bindingDeclInfo struct {
+	names map[string]bool // names bound by the binding decl; possibly empty
+	stmt  ast.Stmt        // the binding decl itself
+}
+
+// createBindingDecl constructs a "binding decl" that implements
+// parameter assignment and declares any named result variables
+// referenced by the callee. It returns nil if there were no
+// unsubstituted parameters.
+//
+// It may not always be possible to create the decl (e.g. due to
+// shadowing), in which case it also returns nil; but if it succeeds,
+// the declaration may be used by reduction strategies to relax the
+// requirement that all parameters have been substituted.
+//
+// For example, a call:
+//
+//	f(a0, a1, a2)
+//
+// where:
+//
+//	func f(p0, p1 T0, p2 T1) { body }
+//
+// reduces to:
+//
+//	{
+//	  var (
+//	    p0, p1 T0 = a0, a1
+//	    p2     T1 = a2
+//	  )
+//	  body
+//	}
+//
+// so long as p0, p1 ∉ freevars(T1) or freevars(a2), and so on,
+// because each spec is statically resolved in sequence and
+// dynamically assigned in sequence. By contrast, all
+// parameters are resolved simultaneously and assigned
+// simultaneously.
+//
+// The pX names should already be blank ("_") if the parameter
+// is unreferenced; this avoids "unreferenced local var" checks.
+//
+// Strategies may impose additional checks on return
+// conversions, labels, defer, etc.
+func createBindingDecl(logf logger, caller *Caller, args []*argument, calleeDecl *ast.FuncDecl, results []*paramInfo) *bindingDeclInfo {
+	// Spread calls are tricky as they may not align with the
+	// parameters' field groupings nor types.
+	// For example, given
+	//   func g() (int, string)
+	// the call
+	//   f(g())
+	// is legal with these decls of f:
+	//   func f(int, string)
+	//   func f(x, y any)
+	//   func f(x, y ...any)
+	// TODO(adonovan): support binding decls for spread calls by
+	// splitting parameter groupings as needed.
+	if lastArg := last(args); lastArg != nil && lastArg.spread {
+		logf("binding decls not yet supported for spread calls")
+		return nil
+	}
+
+	var (
+		specs []ast.Spec
+		names = make(map[string]bool) // names defined by previous specs
+	)
+	// shadow reports whether any name referenced by spec is
+	// shadowed by a name declared by a previous spec (since,
+	// unlike parameters, each spec of a var decl is within the
+	// scope of the previous specs).
+	shadow := func(spec *ast.ValueSpec) bool {
+		// Compute union of free names of type and values
+		// and detect shadowing. Values is the arguments
+		// (caller syntax), so we can use type info.
+		// But Type is the untyped callee syntax,
+		// so we have to use a syntax-only algorithm.
+		const includeComplitIdents = true
+		free := free.Names(spec.Type, includeComplitIdents)
+		for _, value := range spec.Values {
+			for name := range freeVars(caller.Info, value) {
+				free[name] = true
+			}
+		}
+		for name := range free {
+			if names[name] {
+				logf("binding decl would shadow free name %q", name)
+				return true
+			}
+		}
+		for _, id := range spec.Names {
+			if id.Name != "_" {
+				names[id.Name] = true
+			}
+		}
+		return false
+	}
+
+	// parameters
+	//
+	// Bind parameters that were not eliminated through
+	// substitution. (Non-nil arguments correspond to the
+	// remaining parameters in calleeDecl.)
+	var values []ast.Expr
+	for _, arg := range args {
+		if arg != nil {
+			values = append(values, arg.expr)
+		}
+	}
+	for _, field := range calleeDecl.Type.Params.List {
+		// Each field (param group) becomes a ValueSpec.
+		spec := &ast.ValueSpec{
+			Names:  cleanNodes(field.Names),
+			Type:   cleanNode(field.Type),
+			Values: values[:len(field.Names)],
+		}
+		values = values[len(field.Names):]
+		if shadow(spec) {
+			return nil
+		}
+		specs = append(specs, spec)
+	}
+	assert(len(values) == 0, "args/params mismatch")
+
+	// results
+	//
+	// Add specs to declare any named result
+	// variables that are referenced by the body.
+	if calleeDecl.Type.Results != nil {
+		resultIdx := 0
+		for _, field := range calleeDecl.Type.Results.List {
+			if field.Names == nil {
+				resultIdx++
+				continue // unnamed field
+			}
+			var names []*ast.Ident
+			for _, id := range field.Names {
+				if len(results[resultIdx].Refs) > 0 {
+					names = append(names, id)
+				}
+				resultIdx++
+			}
+			if len(names) > 0 {
+				spec := &ast.ValueSpec{
+					Names: cleanNodes(names),
+					Type:  cleanNode(field.Type),
+				}
+				if shadow(spec) {
+					return nil
+				}
+				specs = append(specs, spec)
+			}
+		}
+	}
+
+	if len(specs) == 0 {
+		logf("binding decl not needed: all parameters substituted")
+		return nil
+	}
+
+	stmt := &ast.DeclStmt{
+		Decl: &ast.GenDecl{
+			Tok:   token.VAR,
+			Specs: specs,
+		},
+	}
+	logf("binding decl: %s", debugFormatNode(caller.Fset, stmt))
+	return &bindingDeclInfo{names: names, stmt: stmt}
+}
+
+// lookup does a symbol lookup in the lexical environment of the caller.
+func (caller *Caller) lookup(name string) types.Object {
+	pos := caller.Call.Pos()
+	for _, n := range caller.path {
+		if scope := scopeFor(caller.Info, n); scope != nil {
+			if _, obj := scope.LookupParent(name, pos); obj != nil {
+				return obj
+			}
+		}
+	}
+	return nil
+}
+
+func scopeFor(info *types.Info, n ast.Node) *types.Scope {
+	// The function body scope (containing not just params)
+	// is associated with the function's type, not body.
+	switch fn := n.(type) {
+	case *ast.FuncDecl:
+		n = fn.Type
+	case *ast.FuncLit:
+		n = fn.Type
+	}
+	return info.Scopes[n]
+}
+
+// -- predicates over expressions --
+
+// freeVars returns the names of all free identifiers of e:
+// those lexically referenced by it but not defined within it.
+// (Fields and methods are not included.)
+func freeVars(info *types.Info, e ast.Expr) map[string]bool {
+	free := make(map[string]bool)
+	ast.Inspect(e, func(n ast.Node) bool {
+		if id, ok := n.(*ast.Ident); ok {
+			// The isField check is so that we don't treat T{f: 0} as a ref to f.
+			if obj, ok := info.Uses[id]; ok && !within(obj.Pos(), e) && !isField(obj) {
+				free[obj.Name()] = true
+			}
+		}
+		return true
+	})
+	return free
+}
+
+// effects reports whether an expression might change the state of the
+// program (through function calls and channel receives) and affect
+// the evaluation of subsequent expressions.
+func (st *state) effects(info *types.Info, expr ast.Expr) bool {
+	effects := false
+	ast.Inspect(expr, func(n ast.Node) bool {
+		switch n := n.(type) {
+		case *ast.FuncLit:
+			return false // prune descent
+
+		case *ast.CallExpr:
+			if info.Types[n.Fun].IsType() {
+				// A conversion T(x) has only the effect of its operand.
+			} else if !typesinternal.CallsPureBuiltin(info, n) {
+				// A handful of built-ins have no effect
+				// beyond those of their arguments.
+				// All other calls (including append, copy, recover)
+				// have unknown effects.
+				//
+				// As with 'pure', there is room for
+				// improvement by inspecting the callee.
+				effects = true
+			}
+
+		case *ast.UnaryExpr:
+			if n.Op == token.ARROW { // <-ch
+				effects = true
+			}
+		}
+		return true
+	})
+
+	// Even if consideration of effects is not desired,
+	// we continue to compute, log, and discard them.
+	if st.opts.IgnoreEffects && effects {
+		effects = false
+		st.opts.Logf("ignoring potential effects of argument %s",
+			debugFormatNode(st.caller.Fset, expr))
+	}
+
+	return effects
+}
+
+// pure reports whether an expression has the same result no matter
+// when it is executed relative to other expressions, so it can be
+// commuted with any other expression or statement without changing
+// its meaning.
+//
+// An expression is considered impure if it reads the contents of any
+// variable, with the exception of "single assignment" local variables
+// (as classified by the provided callback), which are never updated
+// after their initialization.
+//
+// Pure does not imply duplicable: for example, new(T) and T{} are
+// pure expressions but both return a different value each time they
+// are evaluated, so they are not safe to duplicate.
+//
+// Purity does not imply freedom from run-time panics. We assume that
+// target programs do not encounter run-time panics nor depend on them
+// for correct operation.
+//
+// TODO(adonovan): add unit tests of this function.
+func pure(info *types.Info, assign1 func(*types.Var) bool, e ast.Expr) bool {
+	var pure func(e ast.Expr) bool
+	pure = func(e ast.Expr) bool {
+		switch e := e.(type) {
+		case *ast.ParenExpr:
+			return pure(e.X)
+
+		case *ast.Ident:
+			if v, ok := info.Uses[e].(*types.Var); ok {
+				// In general variables are impure
+				// as they may be updated, but
+				// single-assignment local variables
+				// never change value.
+				//
+				// We assume all package-level variables
+				// may be updated, but for non-exported
+				// ones we could do better by analyzing
+				// the complete package.
+				return !isPkgLevel(v) && assign1(v)
+			}
+
+			// All other kinds of reference are pure.
+			return true
+
+		case *ast.FuncLit:
+			// A function literal may allocate a closure that
+			// references mutable variables, but mutation
+			// cannot be observed without calling the function,
+			// and calls are considered impure.
+			return true
+
+		case *ast.BasicLit:
+			return true
+
+		case *ast.UnaryExpr: // + - ! ^ & but not <-
+			return e.Op != token.ARROW && pure(e.X)
+
+		case *ast.BinaryExpr: // arithmetic, shifts, comparisons, &&/||
+			return pure(e.X) && pure(e.Y)
+
+		case *ast.CallExpr:
+			// A conversion is as pure as its operand.
+			if info.Types[e.Fun].IsType() {
+				return pure(e.Args[0])
+			}
+
+			// Calls to some built-ins are as pure as their arguments.
+			if typesinternal.CallsPureBuiltin(info, e) {
+				for _, arg := range e.Args {
+					if !pure(arg) {
+						return false
+					}
+				}
+				return true
+			}
+
+			// All other calls are impure, so we can
+			// reject them without even looking at e.Fun.
+			//
+			// More sophisticated analysis could infer purity in
+			// commonly used functions such as strings.Contains;
+			// perhaps we could offer the client a hook so that
+			// go/analysis-based implementation could exploit the
+			// results of a purity analysis. But that would make
+			// the inliner's choices harder to explain.
+			return false
+
+		case *ast.CompositeLit:
+			// T{...} is as pure as its elements.
+			for _, elt := range e.Elts {
+				if kv, ok := elt.(*ast.KeyValueExpr); ok {
+					if !pure(kv.Value) {
+						return false
+					}
+					if id, ok := kv.Key.(*ast.Ident); ok {
+						if v, ok := info.Uses[id].(*types.Var); ok && v.IsField() {
+							continue // struct {field: value}
+						}
+					}
+					// map/slice/array {key: value}
+					if !pure(kv.Key) {
+						return false
+					}
+
+				} else if !pure(elt) {
+					return false
+				}
+			}
+			return true
+
+		case *ast.SelectorExpr:
+			if seln, ok := info.Selections[e]; ok {
+				// See types.SelectionKind for background.
+				switch seln.Kind() {
+				case types.MethodExpr:
+					// A method expression T.f acts like a
+					// reference to a func decl, so it is pure.
+					return true
+
+				case types.MethodVal, types.FieldVal:
+					// A field or method selection x.f is pure
+					// if x is pure and the selection does
+					// not indirect a pointer.
+					return !indirectSelection(seln) && pure(e.X)
+
+				default:
+					panic(seln)
+				}
+			} else {
+				// A qualified identifier is
+				// treated like an unqualified one.
+				return pure(e.Sel)
+			}
+
+		case *ast.StarExpr:
+			return false // *ptr depends on the state of the heap
+
+		default:
+			return false
+		}
+	}
+	return pure(e)
+}
+
+// duplicable reports whether it is appropriate for the expression to
+// be freely duplicated.
+//
+// Given the declaration
+//
+//	func f(x T) T { return x + g() + x }
+//
+// an argument y is considered duplicable if we would wish to see a
+// call f(y) simplified to y+g()+y. This is true for identifiers,
+// integer literals, unary negation, and selectors x.f where x is not
+// a pointer. But we would not wish to duplicate expressions that:
+// - have side effects (e.g. nearly all calls),
+// - are not referentially transparent (e.g. &T{}, ptr.field, *ptr), or
+// - are long (e.g. "huge string literal").
+func duplicable(info *types.Info, e ast.Expr) bool {
+	switch e := e.(type) {
+	case *ast.ParenExpr:
+		return duplicable(info, e.X)
+
+	case *ast.Ident:
+		return true
+
+	case *ast.BasicLit:
+		v := info.Types[e].Value
+		switch e.Kind {
+		case token.INT:
+			return true // any int
+		case token.STRING:
+			return consteq(v, kZeroString) // only ""
+		case token.FLOAT:
+			return consteq(v, kZeroFloat) || consteq(v, kOneFloat) // only 0.0 or 1.0
+		}
+
+	case *ast.UnaryExpr: // e.g. +1, -1
+		return (e.Op == token.ADD || e.Op == token.SUB) && duplicable(info, e.X)
+
+	case *ast.CompositeLit:
+		// Empty struct or array literals T{} are duplicable.
+		// (Non-empty literals are too verbose, and slice/map
+		// literals allocate indirect variables.)
+		if len(e.Elts) == 0 {
+			switch info.TypeOf(e).Underlying().(type) {
+			case *types.Struct, *types.Array:
+				return true
+			}
+		}
+		return false
+
+	case *ast.CallExpr:
+		// Treat type conversions as duplicable if they do not observably allocate.
+		// The only cases of observable allocations are
+		// the `[]byte(string)` and `[]rune(string)` conversions.
+		//
+		// Duplicating string([]byte) conversions increases
+		// allocation but doesn't change behavior, but the
+		// reverse, []byte(string), allocates a distinct array,
+		// which is observable.
+
+		if !info.Types[e.Fun].IsType() { // check whether e.Fun is a type conversion
+			return false
+		}
+
+		fun := info.TypeOf(e.Fun)
+		arg := info.TypeOf(e.Args[0])
+
+		switch fun := fun.Underlying().(type) {
+		case *types.Slice:
+			// Do not mark []byte(string) and []rune(string) as duplicable.
+			elem, ok := fun.Elem().Underlying().(*types.Basic)
+			if ok && (elem.Kind() == types.Rune || elem.Kind() == types.Byte) {
+				from, ok := arg.Underlying().(*types.Basic)
+				isString := ok && from.Info()&types.IsString != 0
+				return !isString
+			}
+		case *types.TypeParam:
+			return false // be conservative
+		}
+		return true
+
+	case *ast.SelectorExpr:
+		if seln, ok := info.Selections[e]; ok {
+			// A field or method selection x.f is referentially
+			// transparent if it does not indirect a pointer.
+			return !indirectSelection(seln)
+		}
+		// A qualified identifier pkg.Name is referentially transparent.
+		return true
+	}
+	return false
+}
+
+func consteq(x, y constant.Value) bool {
+	return constant.Compare(x, token.EQL, y)
+}
+
+var (
+	kZeroInt    = constant.MakeInt64(0)
+	kZeroString = constant.MakeString("")
+	kZeroFloat  = constant.MakeFloat64(0.0)
+	kOneFloat   = constant.MakeFloat64(1.0)
+)
+
+// -- inline helpers --
+
+func assert(cond bool, msg string) {
+	if !cond {
+		panic(msg)
+	}
+}
+
+// blanks returns a slice of n > 0 blank identifiers.
+func blanks[E ast.Expr](n int) []E {
+	if n == 0 {
+		panic("blanks(0)")
+	}
+	res := make([]E, n)
+	for i := range res {
+		res[i] = ast.Expr(makeIdent("_")).(E) // ugh
+	}
+	return res
+}
+
+func makeIdent(name string) *ast.Ident {
+	return &ast.Ident{Name: name}
+}
+
+// importedPkgName returns the PkgName object declared by an ImportSpec.
+// TODO(adonovan): make this a method of types.Info (#62037).
+func importedPkgName(info *types.Info, imp *ast.ImportSpec) (*types.PkgName, bool) {
+	var obj types.Object
+	if imp.Name != nil {
+		obj = info.Defs[imp.Name]
+	} else {
+		obj = info.Implicits[imp]
+	}
+	pkgname, ok := obj.(*types.PkgName)
+	return pkgname, ok
+}
+
+func isPkgLevel(obj types.Object) bool {
+	// TODO(adonovan): consider using the simpler obj.Parent() ==
+	// obj.Pkg().Scope() instead. But be sure to test carefully
+	// with instantiations of generics.
+	return obj.Pkg().Scope().Lookup(obj.Name()) == obj
+}
+
+// callContext returns the two nodes immediately enclosing the call
+// (specified as a PathEnclosingInterval), ignoring parens.
+func callContext(callPath []ast.Node) (parent, grandparent ast.Node) {
+	_ = callPath[0].(*ast.CallExpr) // sanity check
+	for _, n := range callPath[1:] {
+		if !is[*ast.ParenExpr](n) {
+			if parent == nil {
+				parent = n
+			} else {
+				return parent, n
+			}
+		}
+	}
+	return parent, nil
+}
+
+// hasLabelConflict reports whether the set of labels of the function
+// enclosing the call (specified as a PathEnclosingInterval)
+// intersects with the set of callee labels.
+func hasLabelConflict(callPath []ast.Node, calleeLabels []string) bool {
+	labels := callerLabels(callPath)
+	for _, label := range calleeLabels {
+		if labels[label] {
+			return true // conflict
+		}
+	}
+	return false
+}
+
+// callerLabels returns the set of control labels in the function (if
+// any) enclosing the call (specified as a PathEnclosingInterval).
+func callerLabels(callPath []ast.Node) map[string]bool {
+	var callerBody *ast.BlockStmt
+	switch f := callerFunc(callPath).(type) {
+	case *ast.FuncDecl:
+		callerBody = f.Body
+	case *ast.FuncLit:
+		callerBody = f.Body
+	}
+	var labels map[string]bool
+	if callerBody != nil {
+		ast.Inspect(callerBody, func(n ast.Node) bool {
+			switch n := n.(type) {
+			case *ast.FuncLit:
+				return false // prune traversal
+			case *ast.LabeledStmt:
+				if labels == nil {
+					labels = make(map[string]bool)
+				}
+				labels[n.Label.Name] = true
+			}
+			return true
+		})
+	}
+	return labels
+}
+
+// callerFunc returns the innermost Func{Decl,Lit} node enclosing the
+// call (specified as a PathEnclosingInterval).
+func callerFunc(callPath []ast.Node) ast.Node {
+	_ = callPath[0].(*ast.CallExpr) // sanity check
+	for _, n := range callPath[1:] {
+		if is[*ast.FuncDecl](n) || is[*ast.FuncLit](n) {
+			return n
+		}
+	}
+	return nil
+}
+
+// callStmt reports whether the function call (specified
+// as a PathEnclosingInterval) appears within an ExprStmt,
+// and returns it if so.
+//
+// If unrestricted, callStmt returns nil if the ExprStmt f() appears
+// in a restricted context (such as "if f(); cond {") where it cannot
+// be replaced by an arbitrary statement. (See "statement theory".)
+func callStmt(callPath []ast.Node, unrestricted bool) *ast.ExprStmt {
+	parent, _ := callContext(callPath)
+	stmt, ok := parent.(*ast.ExprStmt)
+	if ok && unrestricted {
+		switch callPath[slices.Index(callPath, ast.Node(stmt))+1].(type) {
+		case *ast.LabeledStmt,
+			*ast.BlockStmt,
+			*ast.CaseClause,
+			*ast.CommClause:
+			// unrestricted
+		default:
+			// TODO(adonovan): handle restricted
+			// XYZStmt.Init contexts (but not ForStmt.Post)
+			// by creating a block around the if/for/switch:
+			// "if f(); cond {"  ->  "{ stmts; if cond {"
+
+			return nil // restricted
+		}
+	}
+	return stmt
+}
+
+// Statement theory
+//
+// These are all the places a statement may appear in the AST:
+//
+// LabeledStmt.Stmt       Stmt      -- any
+// BlockStmt.List       []Stmt      -- any (but see switch/select)
+// IfStmt.Init            Stmt?     -- simple
+// IfStmt.Body            BlockStmt
+// IfStmt.Else            Stmt?     -- IfStmt or BlockStmt
+// CaseClause.Body      []Stmt      -- any
+// SwitchStmt.Init        Stmt?     -- simple
+// SwitchStmt.Body        BlockStmt -- CaseClauses only
+// TypeSwitchStmt.Init    Stmt?     -- simple
+// TypeSwitchStmt.Assign  Stmt      -- AssignStmt(TypeAssertExpr) or ExprStmt(TypeAssertExpr)
+// TypeSwitchStmt.Body    BlockStmt -- CaseClauses only
+// CommClause.Comm        Stmt?     -- SendStmt or ExprStmt(UnaryExpr) or AssignStmt(UnaryExpr)
+// CommClause.Body      []Stmt      -- any
+// SelectStmt.Body        BlockStmt -- CommClauses only
+// ForStmt.Init           Stmt?     -- simple
+// ForStmt.Post           Stmt?     -- simple
+// ForStmt.Body           BlockStmt
+// RangeStmt.Body         BlockStmt
+//
+// simple = AssignStmt | SendStmt | IncDecStmt | ExprStmt.
+//
+// A BlockStmt cannot replace an ExprStmt in
+// {If,Switch,TypeSwitch}Stmt.Init or ForStmt.Post.
+// That is allowed only within:
+//   LabeledStmt.Stmt       Stmt
+//   BlockStmt.List       []Stmt
+//   CaseClause.Body      []Stmt
+//   CommClause.Body      []Stmt
+
+// replaceNode performs a destructive update of the tree rooted at
+// root, replacing each occurrence of "from" with "to". If to is nil and
+// the element is within a slice, the slice element is removed.
+//
+// The root itself cannot be replaced; an attempt will panic.
+//
+// This function must not be called on the caller's syntax tree.
+//
+// TODO(adonovan): polish this up and move it to astutil package.
+// TODO(adonovan): needs a unit test.
+func replaceNode(root ast.Node, from, to ast.Node) {
+	if from == nil {
+		panic("from == nil")
+	}
+	if reflect.ValueOf(from).IsNil() {
+		panic(fmt.Sprintf("from == (%T)(nil)", from))
+	}
+	if from == root {
+		panic("from == root")
+	}
+	found := false
+	var parent reflect.Value // parent variable of interface type, containing a pointer
+	var visit func(reflect.Value)
+	visit = func(v reflect.Value) {
+		switch v.Kind() {
+		case reflect.Pointer:
+			if v.Interface() == from {
+				found = true
+
+				// If v is a struct field or array element
+				// (e.g. Field.Comment or Field.Names[i])
+				// then it is addressable (a pointer variable).
+				//
+				// But if it was the value an interface
+				// (e.g. *ast.Ident within ast.Node)
+				// then it is non-addressable, and we need
+				// to set the enclosing interface (parent).
+				if !v.CanAddr() {
+					v = parent
+				}
+
+				// to=nil => use zero value
+				var toV reflect.Value
+				if to != nil {
+					toV = reflect.ValueOf(to)
+				} else {
+					toV = reflect.Zero(v.Type()) // e.g. ast.Expr(nil)
+				}
+				v.Set(toV)
+
+			} else if !v.IsNil() {
+				switch v.Interface().(type) {
+				case *ast.Object, *ast.Scope:
+					// Skip fields of types potentially involved in cycles.
+				default:
+					visit(v.Elem())
+				}
+			}
+
+		case reflect.Struct:
+			for i := range v.Type().NumField() {
+				visit(v.Field(i))
+			}
+
+		case reflect.Slice:
+			compact := false
+			for i := range v.Len() {
+				visit(v.Index(i))
+				if v.Index(i).IsNil() {
+					compact = true
+				}
+			}
+			if compact {
+				// Elements were deleted. Eliminate nils.
+				// (Do this is a second pass to avoid
+				// unnecessary writes in the common case.)
+				j := 0
+				for i := range v.Len() {
+					if !v.Index(i).IsNil() {
+						v.Index(j).Set(v.Index(i))
+						j++
+					}
+				}
+				v.SetLen(j)
+			}
+		case reflect.Interface:
+			parent = v
+			visit(v.Elem())
+
+		case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer:
+			panic(v) // unreachable in AST
+		default:
+			// bool, string, number: nop
+		}
+		parent = reflect.Value{}
+	}
+	visit(reflect.ValueOf(root))
+	if !found {
+		panic(fmt.Sprintf("%T not found", from))
+	}
+}
+
+// cleanNode returns a clone of node with positions cleared.
+//
+// It should be used for any callee nodes that are formatted using the caller
+// file set.
+func cleanNode[T ast.Node](node T) T {
+	clone := internalastutil.CloneNode(node)
+	clearPositions(clone)
+	return clone
+}
+
+func cleanNodes[T ast.Node](nodes []T) []T {
+	var clean []T
+	for _, node := range nodes {
+		clean = append(clean, cleanNode(node))
+	}
+	return clean
+}
+
+// clearPositions destroys token.Pos information within the tree rooted at root,
+// as positions in callee trees may cause caller comments to be emitted prematurely.
+//
+// In general it isn't safe to clear a valid Pos because some of them
+// (e.g. CallExpr.Ellipsis, TypeSpec.Assign) are significant to
+// go/printer, so this function sets each non-zero Pos to 1, which
+// suffices to avoid advancing the printer's comment cursor.
+//
+// This function mutates its argument; do not invoke on caller syntax.
+//
+// TODO(adonovan): remove this horrendous workaround when #20744 is finally fixed.
+func clearPositions(root ast.Node) {
+	posType := reflect.TypeFor[token.Pos]()
+	ast.Inspect(root, func(n ast.Node) bool {
+		if n != nil {
+			v := reflect.ValueOf(n).Elem() // deref the pointer to struct
+			fields := v.Type().NumField()
+			for i := range fields {
+				f := v.Field(i)
+				// Clearing Pos arbitrarily is destructive,
+				// as its presence may be semantically significant
+				// (e.g. CallExpr.Ellipsis, TypeSpec.Assign)
+				// or affect formatting preferences (e.g. GenDecl.Lparen).
+				//
+				// Note: for proper formatting, it may be necessary to be selective
+				// about which positions we set to 1 vs which we set to token.NoPos.
+				// (e.g. we can set most to token.NoPos, save the few that are
+				// significant).
+				if f.Type() == posType {
+					if f.Interface() != token.NoPos {
+						f.Set(reflect.ValueOf(token.Pos(1)))
+					}
+				}
+			}
+		}
+		return true
+	})
+}
+
+// findIdent finds the Ident beneath root that has the given pos.
+// It returns the path to the ident (excluding the ident), and the ident
+// itself, where the path is the sequence of ast.Nodes encountered in a
+// depth-first search to find ident.
+func findIdent(root ast.Node, pos token.Pos) ([]ast.Node, *ast.Ident) {
+	// TODO(adonovan): opt: skip subtrees that don't contain pos.
+	var (
+		path  []ast.Node
+		found *ast.Ident
+	)
+	ast.Inspect(root, func(n ast.Node) bool {
+		if found != nil {
+			return false
+		}
+		if n == nil {
+			path = path[:len(path)-1]
+			return false
+		}
+		if id, ok := n.(*ast.Ident); ok {
+			if id.Pos() == pos {
+				found = id
+				return true
+			}
+		}
+		path = append(path, n)
+		return true
+	})
+	if found == nil {
+		panic(fmt.Sprintf("findIdent %d not found in %s",
+			pos, debugFormatNode(token.NewFileSet(), root)))
+	}
+	return path, found
+}
+
+func prepend[T any](elem T, slice ...T) []T {
+	return append([]T{elem}, slice...)
+}
+
+// debugFormatNode formats a node or returns a formatting error.
+// Its sloppy treatment of errors is appropriate only for logging.
+func debugFormatNode(fset *token.FileSet, n ast.Node) string {
+	var out strings.Builder
+	if err := format.Node(&out, fset, n); err != nil {
+		out.WriteString(err.Error())
+	}
+	return out.String()
+}
+
+func shallowCopy[T any](ptr *T) *T {
+	copy := *ptr
+	return ©
+}
+
+// ∀
+func forall[T any](list []T, f func(i int, x T) bool) bool {
+	for i, x := range list {
+		if !f(i, x) {
+			return false
+		}
+	}
+	return true
+}
+
+// ∃
+func exists[T any](list []T, f func(i int, x T) bool) bool {
+	for i, x := range list {
+		if f(i, x) {
+			return true
+		}
+	}
+	return false
+}
+
+// last returns the last element of a slice, or zero if empty.
+func last[T any](slice []T) T {
+	n := len(slice)
+	if n > 0 {
+		return slice[n-1]
+	}
+	return *new(T)
+}
+
+// declares returns the set of lexical names declared by a
+// sequence of statements from the same block, excluding sub-blocks.
+// (Lexical names do not include control labels.)
+func declares(stmts []ast.Stmt) map[string]bool {
+	names := make(map[string]bool)
+	for _, stmt := range stmts {
+		switch stmt := stmt.(type) {
+		case *ast.DeclStmt:
+			for _, spec := range stmt.Decl.(*ast.GenDecl).Specs {
+				switch spec := spec.(type) {
+				case *ast.ValueSpec:
+					for _, id := range spec.Names {
+						names[id.Name] = true
+					}
+				case *ast.TypeSpec:
+					names[spec.Name.Name] = true
+				}
+			}
+
+		case *ast.AssignStmt:
+			if stmt.Tok == token.DEFINE {
+				for _, lhs := range stmt.Lhs {
+					names[lhs.(*ast.Ident).Name] = true
+				}
+			}
+		}
+	}
+	delete(names, "_")
+	return names
+}
+
+// A importNameFunc is used to query local import names in the caller, in a
+// particular shadowing context.
+//
+// The shadow map contains additional names shadowed in the inlined code, at
+// the position the local import name is to be used. The shadow map only needs
+// to contain newly introduced names in the inlined code; names shadowed at the
+// caller are handled automatically.
+type importNameFunc = func(pkgPath string, shadow shadowMap) string
+
+// assignStmts rewrites a statement assigning the results of a call into zero
+// or more statements that assign its return operands, or (nil, false) if no
+// such rewrite is possible. The set of bindings created by the result of
+// assignStmts is the same as the set of bindings created by the callerStmt.
+//
+// The callee must contain exactly one return statement.
+//
+// This is (once again) a surprisingly complex task. For example, depending on
+// types and existing bindings, the assignment
+//
+//	a, b := f()
+//
+// could be rewritten as:
+//
+//	a, b := 1, 2
+//
+// but may need to be written as:
+//
+//	a, b := int8(1), int32(2)
+//
+// In the case where the return statement within f is a spread call to another
+// function g(), we cannot explicitly convert the return values inline, and so
+// it may be necessary to split the declaration and assignment of variables
+// into separate statements:
+//
+//	a, b := g()
+//
+// or
+//
+//	var a int32
+//	a, b = g()
+//
+// or
+//
+//	var (
+//		a int8
+//		b int32
+//	)
+//	a, b = g()
+//
+// Note: assignStmts may return (nil, true) if it determines that the rewritten
+// assignment consists only of _ = nil assignments.
+func (st *state) assignStmts(callerStmt *ast.AssignStmt, returnOperands []ast.Expr, importName importNameFunc) ([]ast.Stmt, bool) {
+	logf, caller, callee := st.opts.Logf, st.caller, &st.callee.impl
+
+	assert(len(callee.Returns) == 1, "unexpected multiple returns")
+	resultInfo := callee.Returns[0]
+
+	// When constructing assign statements, we need to make sure that we don't
+	// modify types on the left-hand side, such as would happen if the type of a
+	// RHS expression does not match the corresponding LHS type at the caller
+	// (due to untyped conversion or interface widening).
+	//
+	// This turns out to be remarkably tricky to handle correctly.
+	//
+	// Substrategies below are labeled as `Substrategy :`.
+
+	// Collect LHS information.
+	var (
+		lhs    []ast.Expr                                // shallow copy of the LHS slice, for mutation
+		defs   = make([]*ast.Ident, len(callerStmt.Lhs)) // indexes in lhs of defining identifiers
+		blanks = make([]bool, len(callerStmt.Lhs))       // indexes in lhs of blank identifiers
+		byType typeutil.Map                              // map of distinct types -> indexes, for writing specs later
+	)
+	for i, expr := range callerStmt.Lhs {
+		lhs = append(lhs, expr)
+		if name, ok := expr.(*ast.Ident); ok {
+			if name.Name == "_" {
+				blanks[i] = true
+				continue // no type
+			}
+
+			if obj, isDef := caller.Info.Defs[name]; isDef {
+				defs[i] = name
+				typ := obj.Type()
+				idxs, _ := byType.At(typ).([]int)
+				idxs = append(idxs, i)
+				byType.Set(typ, idxs)
+			}
+		}
+	}
+
+	// Collect RHS information
+	//
+	// The RHS is either a parallel assignment or spread assignment, but by
+	// looping over both callerStmt.Rhs and returnOperands we handle both.
+	var (
+		rhs             []ast.Expr              // new RHS of assignment, owned by the inliner
+		callIdx         = -1                    // index of the call among the original RHS
+		nilBlankAssigns = make(map[int]unit)    // indexes in rhs of _ = nil assignments, which can be deleted
+		freeNames       = make(map[string]bool) // free(ish) names among rhs expressions
+		nonTrivial      = make(map[int]bool)    // indexes in rhs of nontrivial result conversions
+	)
+	const includeComplitIdents = true
+
+	for i, expr := range callerStmt.Rhs {
+		if expr == caller.Call {
+			assert(callIdx == -1, "malformed (duplicative) AST")
+			callIdx = i
+			for j, returnOperand := range returnOperands {
+				maps.Copy(freeNames, free.Names(returnOperand, includeComplitIdents))
+				rhs = append(rhs, returnOperand)
+				if resultInfo[j]&nonTrivialResult != 0 {
+					nonTrivial[i+j] = true
+				}
+				if blanks[i+j] && resultInfo[j]&untypedNilResult != 0 {
+					nilBlankAssigns[i+j] = unit{}
+				}
+			}
+		} else {
+			// We must clone before clearing positions, since e came from the caller.
+			expr = internalastutil.CloneNode(expr)
+			clearPositions(expr)
+			maps.Copy(freeNames, free.Names(expr, includeComplitIdents))
+			rhs = append(rhs, expr)
+		}
+	}
+	assert(callIdx >= 0, "failed to find call in RHS")
+
+	// Substrategy "splice": Check to see if we can simply splice in the result
+	// expressions from the callee, such as simplifying
+	//
+	//  x, y := f()
+	//
+	// to
+	//
+	//  x, y := e1, e2
+	//
+	// where the types of x and y match the types of e1 and e2.
+	//
+	// This works as long as we don't need to write any additional type
+	// information.
+	if len(nonTrivial) == 0 { // no non-trivial conversions to worry about
+
+		logf("substrategy: splice assignment")
+		return []ast.Stmt{&ast.AssignStmt{
+			Lhs:    lhs,
+			Tok:    callerStmt.Tok,
+			TokPos: callerStmt.TokPos,
+			Rhs:    rhs,
+		}}, true
+	}
+
+	// Inlining techniques below will need to write type information in order to
+	// preserve the correct types of LHS identifiers.
+	//
+	// typeExpr is a simple helper to write out type expressions. It currently
+	// handles (possibly qualified) type names.
+	//
+	// TODO(rfindley):
+	//   1. expand this to handle more type expressions.
+	//   2. refactor to share logic with callee rewriting.
+	universeAny := types.Universe.Lookup("any")
+	typeExpr := func(typ types.Type, shadow shadowMap) ast.Expr {
+		var (
+			typeName string
+			obj      *types.TypeName // nil for basic types
+		)
+		if tname := typesinternal.TypeNameFor(typ); tname != nil {
+			obj = tname
+			typeName = tname.Name()
+		}
+
+		// Special case: check for universe "any".
+		// TODO(golang/go#66921): this may become unnecessary if any becomes a proper alias.
+		if typ == universeAny.Type() {
+			typeName = "any"
+		}
+
+		if typeName == "" {
+			return nil
+		}
+
+		if obj == nil || obj.Pkg() == nil || obj.Pkg() == caller.Types { // local type or builtin
+			if shadow[typeName] != 0 {
+				logf("cannot write shadowed type name %q", typeName)
+				return nil
+			}
+			obj, _ := caller.lookup(typeName).(*types.TypeName)
+			if obj != nil && types.Identical(obj.Type(), typ) {
+				return ast.NewIdent(typeName)
+			}
+		} else if pkgName := importName(obj.Pkg().Path(), shadow); pkgName != "" {
+			return &ast.SelectorExpr{
+				X:   ast.NewIdent(pkgName),
+				Sel: ast.NewIdent(typeName),
+			}
+		}
+		return nil
+	}
+
+	// Substrategy "spread": in the case of a spread call (func f() (T1, T2) return
+	// g()), since we didn't hit the 'splice' substrategy, there must be some
+	// non-declaring expression on the LHS. Simplify this by pre-declaring
+	// variables, rewriting
+	//
+	//   x, y := f()
+	//
+	// to
+	//
+	//  var x int
+	//  x, y = g()
+	//
+	// Which works as long as the predeclared variables do not overlap with free
+	// names on the RHS.
+	if len(rhs) != len(lhs) {
+		assert(len(rhs) == 1 && len(returnOperands) == 1, "expected spread call")
+
+		for _, id := range defs {
+			if id != nil && freeNames[id.Name] {
+				// By predeclaring variables, we're changing them to be in scope of the
+				// RHS. We can't do this if their names are free on the RHS.
+				return nil, false
+			}
+		}
+
+		// Write out the specs, being careful to avoid shadowing free names in
+		// their type expressions.
+		var (
+			specs    []ast.Spec
+			specIdxs []int
+			shadow   = make(shadowMap)
+		)
+		failed := false
+		byType.Iterate(func(typ types.Type, v any) {
+			if failed {
+				return
+			}
+			idxs := v.([]int)
+			specIdxs = append(specIdxs, idxs[0])
+			texpr := typeExpr(typ, shadow)
+			if texpr == nil {
+				failed = true
+				return
+			}
+			spec := &ast.ValueSpec{
+				Type: texpr,
+			}
+			for _, idx := range idxs {
+				spec.Names = append(spec.Names, ast.NewIdent(defs[idx].Name))
+			}
+			specs = append(specs, spec)
+		})
+		if failed {
+			return nil, false
+		}
+		logf("substrategy: spread assignment")
+		return []ast.Stmt{
+			&ast.DeclStmt{
+				Decl: &ast.GenDecl{
+					Tok:   token.VAR,
+					Specs: specs,
+				},
+			},
+			&ast.AssignStmt{
+				Lhs: callerStmt.Lhs,
+				Tok: token.ASSIGN,
+				Rhs: returnOperands,
+			},
+		}, true
+	}
+
+	assert(len(lhs) == len(rhs), "mismatching LHS and RHS")
+
+	// Substrategy "convert": write out RHS expressions with explicit type conversions
+	// as necessary, rewriting
+	//
+	//  x, y := f()
+	//
+	// to
+	//
+	//  x, y := 1, int32(2)
+	//
+	// As required to preserve types.
+	//
+	// In the special case of _ = nil, which is disallowed by the type checker
+	// (since nil has no default type), we delete the assignment.
+	var origIdxs []int // maps back to original indexes after lhs and rhs are pruned
+	i := 0
+	for j := range lhs {
+		if _, ok := nilBlankAssigns[j]; !ok {
+			lhs[i] = lhs[j]
+			rhs[i] = rhs[j]
+			origIdxs = append(origIdxs, j)
+			i++
+		}
+	}
+	lhs = lhs[:i]
+	rhs = rhs[:i]
+
+	if len(lhs) == 0 {
+		logf("trivial assignment after pruning nil blanks assigns")
+		// After pruning, we have no remaining assignments.
+		// Signal this by returning a non-nil slice of statements.
+		return nil, true
+	}
+
+	// Write out explicit conversions as necessary.
+	//
+	// A conversion is necessary if the LHS is being defined, and the RHS return
+	// involved a nontrivial implicit conversion.
+	for i, expr := range rhs {
+		idx := origIdxs[i]
+		if nonTrivial[idx] && defs[idx] != nil {
+			typ := caller.Info.TypeOf(lhs[i])
+			texpr := typeExpr(typ, nil)
+			if texpr == nil {
+				return nil, false
+			}
+			if _, ok := texpr.(*ast.StarExpr); ok {
+				// TODO(rfindley): is this necessary? Doesn't the formatter add these parens?
+				texpr = &ast.ParenExpr{X: texpr} // *T -> (*T)   so that (*T)(x) is valid
+			}
+			rhs[i] = &ast.CallExpr{
+				Fun:  texpr,
+				Args: []ast.Expr{expr},
+			}
+		}
+	}
+	logf("substrategy: convert assignment")
+	return []ast.Stmt{&ast.AssignStmt{
+		Lhs: lhs,
+		Tok: callerStmt.Tok,
+		Rhs: rhs,
+	}}, true
+}
+
+// tailCallSafeReturn reports whether the callee's return statements may be safely
+// used to return from the function enclosing the caller (which must exist).
+func tailCallSafeReturn(caller *Caller, calleeSymbol *types.Func, callee *gobCallee) bool {
+	// It is safe if all callee returns involve only trivial conversions.
+	if !hasNonTrivialReturn(callee.Returns) {
+		return true
+	}
+
+	var callerType types.Type
+	// Find type of innermost function enclosing call.
+	// (Beware: Caller.enclosingFunc is the outermost.)
+loop:
+	for _, n := range caller.path {
+		switch f := n.(type) {
+		case *ast.FuncDecl:
+			callerType = caller.Info.ObjectOf(f.Name).Type()
+			break loop
+		case *ast.FuncLit:
+			callerType = caller.Info.TypeOf(f)
+			break loop
+		}
+	}
+
+	// Non-trivial return conversions in the callee are permitted
+	// if the same non-trivial conversion would occur after inlining,
+	// i.e. if the caller and callee results tuples are identical.
+	callerResults := callerType.(*types.Signature).Results()
+	calleeResults := calleeSymbol.Type().(*types.Signature).Results()
+	return types.Identical(callerResults, calleeResults)
+}
+
+// hasNonTrivialReturn reports whether any of the returns involve a nontrivial
+// implicit conversion of a result expression.
+func hasNonTrivialReturn(returnInfo [][]returnOperandFlags) bool {
+	for _, resultInfo := range returnInfo {
+		for _, r := range resultInfo {
+			if r&nonTrivialResult != 0 {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+type unit struct{} // for representing sets as maps
diff --git a/vendor/golang.org/x/tools/internal/refactor/inline/util.go b/vendor/golang.org/x/tools/internal/refactor/inline/util.go
new file mode 100644
index 00000000..5f895cce
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/refactor/inline/util.go
@@ -0,0 +1,169 @@
+// Copyright 2023 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 inline
+
+// This file defines various common helpers.
+
+import (
+	"go/ast"
+	"go/constant"
+	"go/token"
+	"go/types"
+	"reflect"
+	"strings"
+
+	"golang.org/x/tools/internal/typeparams"
+)
+
+func is[T any](x any) bool {
+	_, ok := x.(T)
+	return ok
+}
+
+func btoi(b bool) int {
+	if b {
+		return 1
+	} else {
+		return 0
+	}
+}
+
+func offsetOf(fset *token.FileSet, pos token.Pos) int {
+	return fset.PositionFor(pos, false).Offset
+}
+
+// objectKind returns an object's kind (e.g. var, func, const, typename).
+func objectKind(obj types.Object) string {
+	return strings.TrimPrefix(strings.ToLower(reflect.TypeOf(obj).String()), "*types.")
+}
+
+// within reports whether pos is within the half-open interval [n.Pos, n.End).
+func within(pos token.Pos, n ast.Node) bool {
+	return n.Pos() <= pos && pos < n.End()
+}
+
+// trivialConversion reports whether it is safe to omit the implicit
+// value-to-variable conversion that occurs in argument passing or
+// result return. The only case currently allowed is converting from
+// untyped constant to its default type (e.g. 0 to int).
+//
+// The reason for this check is that converting from A to B to C may
+// yield a different result than converting A directly to C: consider
+// 0 to int32 to any.
+//
+// trivialConversion under-approximates trivial conversions, as unfortunately
+// go/types does not record the type of an expression *before* it is implicitly
+// converted, and therefore it cannot distinguish typed constant
+// expressions from untyped constant expressions. For example, in the
+// expression `c + 2`, where c is a uint32 constant, trivialConversion does not
+// detect that the default type of this expression is actually uint32, not untyped
+// int.
+//
+// We could, of course, do better here by reverse engineering some of go/types'
+// constant handling. That may or may not be worthwhile.
+//
+// Example: in func f() int32 { return 0 },
+// the type recorded for 0 is int32, not untyped int;
+// although it is Identical to the result var,
+// the conversion is non-trivial.
+func trivialConversion(fromValue constant.Value, from, to types.Type) bool {
+	if fromValue != nil {
+		var defaultType types.Type
+		switch fromValue.Kind() {
+		case constant.Bool:
+			defaultType = types.Typ[types.Bool]
+		case constant.String:
+			defaultType = types.Typ[types.String]
+		case constant.Int:
+			defaultType = types.Typ[types.Int]
+		case constant.Float:
+			defaultType = types.Typ[types.Float64]
+		case constant.Complex:
+			defaultType = types.Typ[types.Complex128]
+		default:
+			return false
+		}
+		return types.Identical(defaultType, to)
+	}
+	return types.Identical(from, to)
+}
+
+func checkInfoFields(info *types.Info) {
+	assert(info.Defs != nil, "types.Info.Defs is nil")
+	assert(info.Implicits != nil, "types.Info.Implicits is nil")
+	assert(info.Scopes != nil, "types.Info.Scopes is nil")
+	assert(info.Selections != nil, "types.Info.Selections is nil")
+	assert(info.Types != nil, "types.Info.Types is nil")
+	assert(info.Uses != nil, "types.Info.Uses is nil")
+	assert(info.FileVersions != nil, "types.Info.FileVersions is nil")
+}
+
+// intersects reports whether the maps' key sets intersect.
+func intersects[K comparable, T1, T2 any](x map[K]T1, y map[K]T2) bool {
+	if len(x) > len(y) {
+		return intersects(y, x)
+	}
+	for k := range x {
+		if _, ok := y[k]; ok {
+			return true
+		}
+	}
+	return false
+}
+
+// convert returns syntax for the conversion T(x).
+func convert(T, x ast.Expr) *ast.CallExpr {
+	// The formatter generally adds parens as needed,
+	// but before go1.22 it had a bug (#63362) for
+	// channel types that requires this workaround.
+	if ch, ok := T.(*ast.ChanType); ok && ch.Dir == ast.RECV {
+		T = &ast.ParenExpr{X: T}
+	}
+	return &ast.CallExpr{
+		Fun:  T,
+		Args: []ast.Expr{x},
+	}
+}
+
+// isPointer reports whether t's core type is a pointer.
+func isPointer(t types.Type) bool {
+	return is[*types.Pointer](typeparams.CoreType(t))
+}
+
+// indirectSelection is like seln.Indirect() without bug #8353.
+func indirectSelection(seln *types.Selection) bool {
+	// Work around bug #8353 in Selection.Indirect when Kind=MethodVal.
+	if seln.Kind() == types.MethodVal {
+		tArg, indirect := effectiveReceiver(seln)
+		if indirect {
+			return true
+		}
+
+		tParam := seln.Obj().Type().Underlying().(*types.Signature).Recv().Type()
+		return isPointer(tArg) && !isPointer(tParam) // implicit *
+	}
+
+	return seln.Indirect()
+}
+
+// effectiveReceiver returns the effective type of the method
+// receiver after all implicit field selections (but not implicit * or
+// & operations) have been applied.
+//
+// The boolean indicates whether any implicit field selection was indirect.
+func effectiveReceiver(seln *types.Selection) (types.Type, bool) {
+	assert(seln.Kind() == types.MethodVal, "not MethodVal")
+	t := seln.Recv()
+	indices := seln.Index()
+	indirect := false
+	for _, index := range indices[:len(indices)-1] {
+		if isPointer(t) {
+			indirect = true
+			t = typeparams.MustDeref(t)
+		}
+		t = typeparams.CoreType(t).(*types.Struct).Field(index).Type()
+	}
+	return t, indirect
+}
diff --git a/vendor/golang.org/x/tools/internal/stdlib/deps.go b/vendor/golang.org/x/tools/internal/stdlib/deps.go
index 0f73c719..b049e5f9 100644
--- a/vendor/golang.org/x/tools/internal/stdlib/deps.go
+++ b/vendor/golang.org/x/tools/internal/stdlib/deps.go
@@ -12,386 +12,387 @@ type pkginfo struct {
 }
 
 var deps = [...]pkginfo{
-	{"archive/tar", "\x03{\x03F>\x01\n\x01&\x01\x01\x02\x05\b\x02\x01\x02\x02\r"},
-	{"archive/zip", "\x02\x04j\x0e\x03\x12\x022>\x01-\x05\x01\x0f\x03\x02\x0f\x04"},
-	{"bufio", "\x03{\x87\x01F\x15"},
-	{"bytes", "~*]\x03\fJ\x02\x02"},
+	{"archive/tar", "\x03|\x03F>\x01\n\x01&\x01\x01\x02\x05\b\x02\x01\x02\x02\r"},
+	{"archive/zip", "\x02\x04k\x0e\x03\x12\x022>\x01-\x05\x01\x0f\x03\x02\x0f\x04"},
+	{"bufio", "\x03|\x87\x01F\x15"},
+	{"bytes", "\x7f*]\x03\fJ\x02\x02"},
 	{"cmp", ""},
-	{"compress/bzip2", "\x02\x02\x81\x02C"},
-	{"compress/flate", "\x02|\x03\x84\x01\f\x034\x02\x03"},
-	{"compress/gzip", "\x02\x04j\x0e\x03\x14pW"},
-	{"compress/lzw", "\x02|\x03\x84\x01"},
-	{"compress/zlib", "\x02\x04j\x0e\x03\x12\x01q"},
-	{"container/heap", "\xc9\x02"},
+	{"compress/bzip2", "\x02\x02\x82\x02C"},
+	{"compress/flate", "\x02}\x03\x84\x01\f\x034\x02\x03"},
+	{"compress/gzip", "\x02\x04k\x0e\x03\x14pW"},
+	{"compress/lzw", "\x02}\x03\x84\x01"},
+	{"compress/zlib", "\x02\x04k\x0e\x03\x12\x01q"},
+	{"container/heap", "\xca\x02"},
 	{"container/list", ""},
 	{"container/ring", ""},
-	{"context", "~]r\x01\x0e"},
-	{"crypto", "\x93\x01rE"},
-	{"crypto/aes", "\x10\v\n\xa5\x02"},
-	{"crypto/cipher", "\x03\"\x01\x01 \x13$+\\"},
-	{"crypto/des", "\x10\x17 7+\xa1\x01\x03"},
-	{"crypto/dsa", "G\x034\x87\x01\r"},
-	{"crypto/ecdh", "\x03\v\r\x11\x04\x17\x03\x10$\x87\x01"},
-	{"crypto/ecdsa", "\x0e\x05\x03\x05\x01\x11\b\v\x06\x01\x03\x0f\x01$\x87\x01\r\x05O\x01"},
-	{"crypto/ed25519", "\x0e \x12\a\x03\t\a$I>E"},
-	{"crypto/elliptic", "5I\x87\x01\r;"},
-	{"crypto/fips140", "$\x05\x9e\x01\x9b\x01"},
-	{"crypto/hkdf", "1\x15\x017\x15"},
-	{"crypto/hmac", "\x1b\x17\x14\x01\x139"},
-	{"crypto/hpke", "\x03\v\x02\x03\x04\x01\r\x01\x05\x1f\x06\a\x01\x01%\x03\x12\x16\x9f\x01\x1d"},
-	{"crypto/internal/boring", "\x0e\x02\x0eu"},
-	{"crypto/internal/boring/bbig", "\x1b\xf7\x01P"},
-	{"crypto/internal/boring/bcache", "\xce\x02\x14"},
+	{"context", "\x7f]r\x01\x0e"},
+	{"crypto", "\x94\x01rE"},
+	{"crypto/aes", "\x10\v\v\xa5\x02"},
+	{"crypto/cipher", "\x03#\x01\x01 \x13$+\\"},
+	{"crypto/des", "\x10\x18 7+\xa1\x01\x03"},
+	{"crypto/dsa", "H\x034\x87\x01\r"},
+	{"crypto/ecdh", "\x03\v\r\x12\x04\x17\x03\x10$\x87\x01"},
+	{"crypto/ecdsa", "\x0e\x05\x03\x05\x01\x12\b\v\x06\x01\x03\x0f\x01$\x87\x01\r\x05O\x01"},
+	{"crypto/ed25519", "\x0e!\x12\a\x03\t\a$I>E"},
+	{"crypto/elliptic", "6I\x87\x01\r;"},
+	{"crypto/fips140", "%\x05\x9e\x01\x9b\x01"},
+	{"crypto/hkdf", "2\x15\x017\x15"},
+	{"crypto/hmac", "\x1b\x18\x14\x01\x139"},
+	{"crypto/hpke", "\x03\v\x02\x03\x04\x01\x0e\x01\x05\x1f\x06\a\x01\x01%\x03\x12\x16\x9f\x01\x1d"},
+	{"crypto/internal/boring", "\x0e\x02\x0ev"},
+	{"crypto/internal/boring/bbig", "\x1b\xf8\x01P"},
+	{"crypto/internal/boring/bcache", "\xcf\x02\x14"},
 	{"crypto/internal/boring/sig", ""},
 	{"crypto/internal/constanttime", ""},
-	{"crypto/internal/cryptotest", "\x03\r\v\t%\x11\x1a\r\x12\x12!\x04\x06\n\x19\x01\x11\x11\x1d\x01\a\x03\x02\b\x02\x01\x05\f"},
-	{"crypto/internal/cryptotest/wycheproof", "\x0e\x12S\x01\f\x01r@\x05\x03\x15\x10"},
-	{"crypto/internal/entropy", "L"},
-	{"crypto/internal/entropy/v1.0.0", "E9\x96\x01:\x14"},
-	{"crypto/internal/fips140", "D:\xc2\x01\v\x17"},
-	{"crypto/internal/fips140/aes", "\x03!\x03\x02\x14\x05\x01\x01\x055\x96\x016"},
-	{"crypto/internal/fips140/aes/gcm", "$\x01\x02\x02\x02\x12\x05\x01\x065\x93\x01"},
-	{"crypto/internal/fips140/alias", "\xe2\x02"},
-	{"crypto/internal/fips140/bigmod", ")\x19\x01\x065\x96\x01"},
-	{"crypto/internal/fips140/check", "$\x0e\a\t\x02\xc1\x01]"},
-	{"crypto/internal/fips140/check/checktest", ")\x97\x02\""},
-	{"crypto/internal/fips140/drbg", "\x03 \x01\x01\x04\x14\x05\n2\x87\x01\x0f9\x01"},
-	{"crypto/internal/fips140/ecdh", "\x03!\x05\x02\n\r<\x87\x01\x0f9"},
-	{"crypto/internal/fips140/ecdsa", "\x03!\x04\x01\x02\a\x03\x06C\x15r\x0f9"},
-	{"crypto/internal/fips140/ed25519", "\x03!\x05\x02\x04\fC\xcc\x01\x03"},
-	{"crypto/internal/fips140/edwards25519", "\x1f\n\a\x12<\x96\x019"},
-	{"crypto/internal/fips140/edwards25519/field", ")\x14\x05<\x96\x01"},
-	{"crypto/internal/fips140/hkdf", "\x03!\x05\t\aE\x15"},
-	{"crypto/internal/fips140/hmac", "\x03!\x15\x01\x01C\x15"},
-	{"crypto/internal/fips140/mldsa", "\x03\x1c\x05\x05\x02\x0e\x01\x03\x05<\x96\x019"},
-	{"crypto/internal/fips140/mlkem", "\x03!\x05\x02\x0f\x03\x05<\xcf\x01"},
-	{"crypto/internal/fips140/nistec", "\x1f\n\r\f<\x96\x01,\r\x15"},
-	{"crypto/internal/fips140/nistec/fiat", ")\x14A\x96\x01"},
-	{"crypto/internal/fips140/pbkdf2", "\x03!\x05\t\aE\x15"},
-	{"crypto/internal/fips140/rsa", "\x03\x1c\x05\x04\x01\x02\x0e\x01\x01\x02A\x15rH"},
-	{"crypto/internal/fips140/sha256", "\x03!\x1e\x01\x065\x15\x81\x01"},
-	{"crypto/internal/fips140/sha3", "\x03!\x19\x05\x01;\x96\x01N"},
-	{"crypto/internal/fips140/sha512", "\x03!\x1e\x01\x065\x15\x81\x01"},
-	{"crypto/internal/fips140/ssh", ")j"},
-	{"crypto/internal/fips140/subtle", "\x1f\b\x1b\xd2\x01"},
-	{"crypto/internal/fips140/tls12", "\x03!\x05\t\a\x02C\x15"},
-	{"crypto/internal/fips140/tls13", "\x03!\x05\b\b\t<\x15"},
-	{"crypto/internal/fips140cache", "\xc0\x02\r."},
+	{"crypto/internal/cryptotest", "\x03\r\v\n%\x11\x1a\r\x12\x12!\x04\x06\n\x19\x01\x11\x11\x1d\x01\a\x03\x02\b\x02\x01\x05\f"},
+	{"crypto/internal/cryptotest/wycheproof", "\x0e\x12T\x01\f\x01r@\x05\x03\x15\x10"},
+	{"crypto/internal/cryptotest/x509limbo", "u\n\x03\xba\x01\x03\x1e"},
+	{"crypto/internal/entropy", "M"},
+	{"crypto/internal/entropy/v1.0.0", "F9\x96\x01:\x14"},
+	{"crypto/internal/fips140", "E:\xc2\x01\v\x17"},
+	{"crypto/internal/fips140/aes", "\x03\"\x03\x02\x14\x05\x01\x01\x055\x96\x016"},
+	{"crypto/internal/fips140/aes/gcm", "%\x01\x02\x02\x02\x12\x05\x01\x065\x93\x01"},
+	{"crypto/internal/fips140/alias", "\xe3\x02"},
+	{"crypto/internal/fips140/bigmod", "*\x19\x01\x065\x96\x01"},
+	{"crypto/internal/fips140/check", "%\x0e\a\t\x02\xc1\x01]"},
+	{"crypto/internal/fips140/check/checktest", "*\x97\x02\""},
+	{"crypto/internal/fips140/drbg", "\x03!\x01\x01\x04\x14\x05\n2\x87\x01\x0f9\x01"},
+	{"crypto/internal/fips140/ecdh", "\x03\"\x05\x02\n\r<\x87\x01\x0f9"},
+	{"crypto/internal/fips140/ecdsa", "\x03\"\x04\x01\x02\a\x03\x06C\x15r\x0f9"},
+	{"crypto/internal/fips140/ed25519", "\x03\"\x05\x02\x04\fC\xcc\x01\x03"},
+	{"crypto/internal/fips140/edwards25519", "\x1f\v\a\x12<\x96\x019"},
+	{"crypto/internal/fips140/edwards25519/field", "*\x14\x05<\x96\x01"},
+	{"crypto/internal/fips140/hkdf", "\x03\"\x05\t\aE\x15"},
+	{"crypto/internal/fips140/hmac", "\x03\"\x15\x01\x01C\x15"},
+	{"crypto/internal/fips140/mldsa", "\x03\x1c\x06\x05\x02\x0e\x01\x03\x05<\x96\x019"},
+	{"crypto/internal/fips140/mlkem", "\x03\"\x05\x02\x0f\x03\x05<\xcf\x01"},
+	{"crypto/internal/fips140/nistec", "\x1f\v\r\f<\x96\x01,\r\x15"},
+	{"crypto/internal/fips140/nistec/fiat", "*\x14A\x96\x01"},
+	{"crypto/internal/fips140/pbkdf2", "\x03\"\x05\t\aE\x15"},
+	{"crypto/internal/fips140/rsa", "\x03\x1c\x06\x04\x01\x02\x0e\x01\x01\x02A\x15rH"},
+	{"crypto/internal/fips140/sha256", "\x03\"\x1e\x01\x065\x15\x81\x01"},
+	{"crypto/internal/fips140/sha3", "\x03\"\x19\x05\x01;\x96\x01N"},
+	{"crypto/internal/fips140/sha512", "\x03\"\x1e\x01\x065\x15\x81\x01"},
+	{"crypto/internal/fips140/ssh", "*j"},
+	{"crypto/internal/fips140/subtle", "\x1f\t\x1b\xd2\x01"},
+	{"crypto/internal/fips140/tls12", "\x03\"\x05\t\a\x02C\x15"},
+	{"crypto/internal/fips140/tls13", "\x03\"\x05\b\b\t<\x15"},
+	{"crypto/internal/fips140cache", "\xc1\x02\r."},
 	{"crypto/internal/fips140deps", ""},
-	{"crypto/internal/fips140deps/byteorder", "\xa9\x01"},
-	{"crypto/internal/fips140deps/cpu", "\xbe\x01\b"},
-	{"crypto/internal/fips140deps/godebug", "\xc7\x01"},
-	{"crypto/internal/fips140deps/time", "\xe2\x02"},
-	{"crypto/internal/fips140hash", ":\x1e;\xcf\x01"},
-	{"crypto/internal/fips140only", "\x17\x14\x0e\x01\x01Xr"},
+	{"crypto/internal/fips140deps/byteorder", "\xaa\x01"},
+	{"crypto/internal/fips140deps/cpu", "\xbf\x01\b"},
+	{"crypto/internal/fips140deps/godebug", "\xc8\x01"},
+	{"crypto/internal/fips140deps/time", "\xe3\x02"},
+	{"crypto/internal/fips140hash", ";\x1e;\xcf\x01"},
+	{"crypto/internal/fips140only", "\x17\x15\x0e\x01\x01Xr"},
 	{"crypto/internal/fips140test", ""},
-	{"crypto/internal/impl", "\xcb\x02"},
-	{"crypto/internal/rand", "\x1b\x10 |>]"},
-	{"crypto/internal/randutil", "\x85\x02\x12"},
-	{"crypto/internal/sysrand", "~r!\"\r\r\x01\x01\r\x06"},
-	{"crypto/internal/sysrand/internal/seccomp", "~"},
-	{"crypto/md5", "\x0e97\x15\x16k"},
-	{"crypto/mldsa", "\x0e%K\x87\x01"},
-	{"crypto/mlkem", "\x0e&"},
-	{"crypto/mlkem/mlkemtest", "4\x13\t."},
-	{"crypto/pbkdf2", "7\x0f\x017\x15"},
-	{"crypto/rand", "\x1b\x10\x1c\x034\x87\x01\rP"},
-	{"crypto/rc4", "' 7\xcc\x01"},
-	{"crypto/rsa", "\x0e\r\x01\f\x10\x0e\x01\x03\t\a$\x03\x124>\f\x01"},
-	{"crypto/sha1", "\x0e\r,\x025\x15\x16\x15V"},
-	{"crypto/sha256", "\x0e\r\x1eZ"},
-	{"crypto/sha3", "\x0e,Y\xcf\x01"},
-	{"crypto/sha512", "\x0e\r X"},
-	{"crypto/subtle", "\x1f\x1e\xa9\x01|"},
-	{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\n\x01\x18\x01\x0f\x01\x01\x03\x01\x01\x01\x01\x02\x01\x02\x01\x1f\x02\x03\x12\x16\x15\t>\x16\x18\r\b\x01\x01\x01\x02\x01\x0e\x06\x03\x01\x15"},
-	{"crypto/tls/internal/fips140tls", "\x17\xb7\x02"},
-	{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x01\x017\x01\x06\x01\x01\x02\x05\x0f\x06\t\x02\x03F\x03;\x01\x02\b\x01\x01\x02\a\x12\x05\x01\x06\a\b\x02\x01\x02\x0f\x02\x01\x01\x02\x04\x01"},
-	{"crypto/x509/pkix", "m\x06\x0e\x91\x019\x11"},
-	{"database/sql", "\x03\nS\x01\x1d\x03\x84\x01\v\a$\x05\b\x02\x03\x01\x0e\x02\x02\x02\x01"},
-	{"database/sql/driver", "\rT\x1d\x03\xba\x01\x0f\x12\a"},
+	{"crypto/internal/impl", "\xcc\x02"},
+	{"crypto/internal/rand", "\x1b\x11 |>]"},
+	{"crypto/internal/randutil", "\x86\x02\x12"},
+	{"crypto/internal/sysrand", "\x7fr!\"\r\r\x01\x01\r\x06"},
+	{"crypto/internal/sysrand/internal/seccomp", "\x7f"},
+	{"crypto/md5", "\x0e:7\x15\x16k"},
+	{"crypto/mldsa", "\x0e&K\x87\x01"},
+	{"crypto/mlkem", "\x0e'"},
+	{"crypto/mlkem/mlkemtest", "5\x13\t."},
+	{"crypto/pbkdf2", "8\x0f\x017\x15"},
+	{"crypto/rand", "\x1b\x11\x1c\x034\x87\x01\rP"},
+	{"crypto/rc4", "( 7\xcc\x01"},
+	{"crypto/rsa", "\x0e\r\x01\r\x10\x0e\x01\x03\t\a$\x03\x124>\f\x01"},
+	{"crypto/sha1", "\x0e\r-\x025\x15\x16\x15V"},
+	{"crypto/sha256", "\x0e\r\x1fZ"},
+	{"crypto/sha3", "\x0e-Y\xcf\x01"},
+	{"crypto/sha512", "\x0e\r!X"},
+	{"crypto/subtle", "\x1f\x1f\xa9\x01|"},
+	{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\v\x01\x18\x01\x0f\x01\x01\x03\x01\x01\x01\x01\x02\x01\x02\x01\x1f\x02\x03\x12\x16\x15\t>\x16\x18\r\b\x01\x01\x01\x02\x01\x0e\x06\x03\x01\x15"},
+	{"crypto/tls/internal/fips140tls", "\x17\xb8\x02"},
+	{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x01\x018\x01\x06\x01\x01\x02\x05\x0f\x06\t\x02\x03F\x03;\x01\x02\b\x01\x01\x02\a\x12\x05\x01\x06\a\b\x02\x01\x02\x0f\x02\x01\x01\x02\x04\x01"},
+	{"crypto/x509/pkix", "n\x06\x0e\x91\x019\x11"},
+	{"database/sql", "\x03\nT\x01\x1d\x03\x84\x01\v\a$\x05\b\x02\x03\x01\x0e\x02\x02\x02\x01"},
+	{"database/sql/driver", "\rU\x1d\x03\xba\x01\x0f\x12\a"},
 	{"database/sql/internal", ""},
-	{"debug/buildinfo", "\x03a\x02\x01\x01\b\x0e\x03h\x1a\x02\x01-\x0f "},
-	{"debug/dwarf", "\x03m\x0e\x03\x84\x013\x11\x01\x01"},
-	{"debug/elf", "\x03\x06Z\r\x0e\x03h\x1b\x01\f\"\x17\x01\x17"},
-	{"debug/gosym", "\x03m\x11#\xa5\x01\x01\x01\x02"},
-	{"debug/macho", "\x03\x06Z\r\x11h\x1c.\x17\x01"},
-	{"debug/pe", "\x03\x06Z\r\x0e\x03h\x1c.\x17\x01\x17"},
-	{"debug/plan9obj", "p\x0e\x03h\x1c."},
-	{"embed", "~*D\x19\x01V"},
+	{"debug/buildinfo", "\x03b\x02\x01\x01\b\x0e\x03h\x1a\x02\x01-\x0f "},
+	{"debug/dwarf", "\x03n\x0e\x03\x84\x013\x11\x01\x01"},
+	{"debug/elf", "\x03\x06[\r\x0e\x03h\x1b\x01\f\"\x17\x01\x17"},
+	{"debug/gosym", "\x03n\x11#\xa5\x01\x01\x01\x02"},
+	{"debug/macho", "\x03\x06[\r\x11h\x1c.\x17\x01"},
+	{"debug/pe", "\x03\x06[\r\x0e\x03h\x1c.\x17\x01\x17"},
+	{"debug/plan9obj", "q\x0e\x03h\x1c."},
+	{"embed", "\x7f*D\x19\x01V"},
 	{"embed/internal/embedtest", ""},
 	{"encoding", ""},
-	{"encoding/ascii85", "\x85\x02E"},
-	{"encoding/asn1", "\x03{\x03h(\x01)\r\x02\x01\x11\x03\x01"},
-	{"encoding/base32", "\x85\x02C\x02"},
-	{"encoding/base64", "\xa9\x01\\C\x02"},
-	{"encoding/binary", "~\x87\x01\f*\r\x05"},
-	{"encoding/csv", "\x02\x01{\x03\x84\x01F\x13\x02"},
-	{"encoding/gob", "\x02i\x05\x0e\x03h\x1c\v\x01\x03\x1f\b\x12\x01\x10\x02"},
-	{"encoding/hex", "~\x03\x84\x01C\x03"},
-	{"encoding/json", "\x03\x01g\n\x01\x01\x02\x01\x01\x03\x03\x84\x016\x0f\x01"},
-	{"encoding/json/internal", "~"},
-	{"encoding/json/internal/jsonflags", "u"},
-	{"encoding/json/internal/jsonopts", "u\x01"},
-	{"encoding/json/internal/jsontest", "\x03f\x15\x03\x83\x01\x01\x012\b\b\x03\x02\x0f"},
-	{"encoding/json/internal/jsonwire", "\x04r\b\x87\x01\f7\x02\x01\x13\x01\x01"},
-	{"encoding/json/jsontext", "\x03r\x01\x01\x02\x05\x87\x01\x03\t\x034\x02\x01\x02\x13"},
-	{"encoding/json/v2", "\x03\x01g\x03\x01\x01\x03\x02\x01\x01\x02\x01\x04\x03\x84\x01\f\x03'\r\x02\x01\x02\x0f\x02\x02"},
-	{"encoding/pem", "\x03l\x0f\x87\x01C\x03"},
-	{"encoding/xml", "\x02\x01h\x13\x03\x84\x016\x05\n\x01\x02\x11\x02"},
-	{"errors", "\xdb\x01\x87\x01"},
-	{"expvar", "tSB\b\v\x17\r\b\x02\x03\x01\x12"},
-	{"flag", "k\x13\x03\x84\x01.\b\x05\b\x02\x01\x11"},
-	{"fmt", "~E)\x19\f\"\b\r\x02\x03\x13"},
-	{"go/ast", "\x03\x01}\x0e\x01u\x03+\b\r\x02\x01\x13\x02"},
-	{"go/build", "\x02\x01{\x03\x01\x02\x02\a\x02\x01\x17 \x04\x02\t\x1c\x13\x01-\x01\x04\x01\a\b\x02\x01\x13\x02\x02"},
-	{"go/build/constraint", "~\xcc\x01\x01\x13\x02"},
-	{"go/constant", "\x81\x01\x0f\x81\x01\x01\x026\x01\x02\x13"},
-	{"go/doc", "\x04}\x01\x05\t>73\x10\x02\x01\x13\x02"},
-	{"go/doc/comment", "\x03~\xc7\x01\x01\x01\x01\x13\x02"},
-	{"go/format", "\x03~\x01\v\x01\x02uF"},
-	{"go/importer", "\x83\x01\a\x01\x01\x04\x01t;"},
-	{"go/internal/gccgoimporter", "\x02\x01a\x1a\x03\x04\v\x01r\x02.\x01\x05\x11\x01\r\b"},
-	{"go/internal/gcimporter", "\x02\x7f\x0f\x010\x140.\x15\x03\x02"},
-	{"go/internal/srcimporter", "\x81\x01\x01\x01\n\x03\x01t.\x01\x05\x12\x02\x15"},
-	{"go/parser", "\x03{\x03\x01\x02\v\x01u\x01-\x06\x12"},
-	{"go/printer", "\x81\x01\x01\x02\x03\tu\f\"\x15\x02\x01\x02\f\x05\x02"},
-	{"go/scanner", "\x03~\x0fu4\x10\x01\x14\x02"},
-	{"go/token", "\x04}\x87\x01@\x02\x03\x01\x10\x02"},
-	{"go/types", "\x03\x01\x06t\x03\x01\x03\b\x03\x02\x0654\x04\x03\t\"\x06\a\b\x01\x01\x01\x02\x01\x10\x02\x02"},
-	{"go/version", "\xcc\x01\x7f"},
-	{"hash", "\x85\x02"},
-	{"hash/adler32", "~\x15\x16"},
-	{"hash/crc32", "~\x15\x16\x15\x8f\x01\x01\x14"},
-	{"hash/crc64", "~\x15\x16\xa4\x01"},
-	{"hash/fnv", "~\x15\x16k"},
-	{"hash/maphash", "\x93\x01\x11>\x80\x01"},
-	{"html", "\xcb\x02\x02\x13"},
-	{"html/template", "\x03q\r\x18.>\x01\n#\x05\x01\x02\x03\n\x02\x01\x02\r\x01\x03\x02"},
-	{"image", "\x02|\x1ei\x0f6\x03\x01"},
+	{"encoding/ascii85", "\x86\x02E"},
+	{"encoding/asn1", "\x03|\x03h(\x01)\r\x02\x01\x11\x03\x01"},
+	{"encoding/base32", "\x86\x02C\x02"},
+	{"encoding/base64", "\xaa\x01\\C\x02"},
+	{"encoding/binary", "\x7f\x87\x01\f*\r\x05"},
+	{"encoding/csv", "\x02\x01|\x03\x84\x01F\x13\x02"},
+	{"encoding/gob", "\x02j\x05\x0e\x03h\x1c\v\x01\x03\x1f\b\x12\x01\x10\x02"},
+	{"encoding/hex", "\x7f\x03\x84\x01C\x03"},
+	{"encoding/json", "\x03\x01h\n\x01\x01\x02\x01\x01\x03\x03\x84\x016\x0f\x01"},
+	{"encoding/json/internal", "\x7f"},
+	{"encoding/json/internal/jsonflags", "v"},
+	{"encoding/json/internal/jsonopts", "v\x01"},
+	{"encoding/json/internal/jsontest", "\x03g\x15\x03\x83\x01\x01\x012\b\b\x03\x02\x0f"},
+	{"encoding/json/internal/jsonwire", "\x04s\b\x87\x01\f7\x02\x01\x13\x01\x01"},
+	{"encoding/json/jsontext", "\x03s\x01\x01\x02\x05\x87\x01\x03\t\x034\x02\x01\x02\x13"},
+	{"encoding/json/v2", "\x03\x01h\x03\x01\x01\x03\x02\x01\x01\x02\x01\x04\x03\x84\x01\f\x03'\r\x02\x01\x02\x0f\x02\x02"},
+	{"encoding/pem", "\x03m\x0f\x87\x01C\x03"},
+	{"encoding/xml", "\x02\x01i\x13\x03\x84\x016\x05\n\x01\x02\x11\x02"},
+	{"errors", "\xdc\x01\x87\x01"},
+	{"expvar", "uSB\b\v\x17\r\b\x02\x03\x01\x12"},
+	{"flag", "l\x13\x03\x84\x01.\b\x05\b\x02\x01\x11"},
+	{"fmt", "\x7fE)\x19\f\"\b\r\x02\x03\x13"},
+	{"go/ast", "\x03\x01~\x0e\x01u\x03+\b\r\x02\x01\x13\x02"},
+	{"go/build", "\x02\x01|\x03\x01\x02\x02\a\x02\x01\x17 \x04\x02\t\x1c\x13\x01-\x01\x04\x01\a\b\x02\x01\x13\x02\x02"},
+	{"go/build/constraint", "\x7f\xcc\x01\x01\x13\x02"},
+	{"go/constant", "\x82\x01\x0f\x81\x01\x01\x026\x01\x02\x13"},
+	{"go/doc", "\x04~\x01\x05\t>73\x10\x02\x01\x13\x02"},
+	{"go/doc/comment", "\x03\x7f\xc7\x01\x01\x01\x01\x13\x02"},
+	{"go/format", "\x03\x7f\x01\v\x01\x02uF"},
+	{"go/importer", "\x84\x01\a\x01\x01\x04\x01t;"},
+	{"go/internal/gccgoimporter", "\x02\x01b\x1a\x03\x04\v\x01r\x02.\x01\x05\x11\x01\r\b"},
+	{"go/internal/gcimporter", "\x02\x80\x01\x0f\x010\x140.\x15\x03\x02"},
+	{"go/internal/srcimporter", "\x82\x01\x01\x01\n\x03\x01t.\x01\x05\x12\x02\x15"},
+	{"go/parser", "\x03|\x03\x01\x02\v\x01u\x01-\x06\x12"},
+	{"go/printer", "\x82\x01\x01\x02\x03\tu\f\"\x15\x02\x01\x02\f\x05\x02"},
+	{"go/scanner", "\x03\x7f\x0fu4\x10\x01\x14\x02"},
+	{"go/token", "\x04~\x87\x01@\x02\x03\x01\x10\x02"},
+	{"go/types", "\x03\x01\x06u\x03\x01\x03\b\x03\x02\x0654\x04\x03\t\"\x06\a\b\x01\x01\x01\x02\x01\x10\x02\x02"},
+	{"go/version", "\xcd\x01\x7f"},
+	{"hash", "\x86\x02"},
+	{"hash/adler32", "\x7f\x15\x16"},
+	{"hash/crc32", "\x7f\x15\x16\x15\x8f\x01\x01\x14"},
+	{"hash/crc64", "\x7f\x15\x16\xa4\x01"},
+	{"hash/fnv", "\x7f\x15\x16k"},
+	{"hash/maphash", "\x94\x01\x11>\x80\x01"},
+	{"html", "\xcc\x02\x02\x13"},
+	{"html/template", "\x03r\r\x18.>\x01\n#\x05\x01\x02\x03\n\x02\x01\x02\r\x01\x03\x02"},
+	{"image", "\x02}\x1ei\x0f6\x03\x01"},
 	{"image/color", ""},
-	{"image/color/palette", "\x9c\x01"},
-	{"image/draw", "\x9b\x01\x01\x04"},
-	{"image/gif", "\x02\x01\x05v\x03\x1a\x01\x01\x01\v\\\x0f"},
-	{"image/internal/imageutil", "\x9b\x01"},
-	{"image/jpeg", "\x02|\x1d\x01\x04e"},
-	{"image/png", "\x02\ag\x11\x12\x02\x06\x01iE"},
-	{"index/suffixarray", "\x03m\x0e\x87\x01\f-\n\x01"},
-	{"internal/abi", "\xc6\x01\x9c\x01"},
-	{"internal/asan", "\xe2\x02"},
-	{"internal/bisect", "\xc0\x02\r\x01"},
-	{"internal/buildcfg", "\x81\x01Hj\x06\x02\x05\n\x01"},
-	{"internal/bytealg", "\xbe\x01\xa4\x01"},
+	{"image/color/palette", "\x9d\x01"},
+	{"image/draw", "\x9c\x01\x01\x04"},
+	{"image/gif", "\x02\x01\x05w\x03\x1a\x01\x01\x01\v\\\x0f"},
+	{"image/internal/imageutil", "\x9c\x01"},
+	{"image/jpeg", "\x02}\x1d\x01\x04e"},
+	{"image/png", "\x02\ah\x11\x12\x02\x06\x01iE"},
+	{"index/suffixarray", "\x03n\x0e\x87\x01\f-\n\x01"},
+	{"internal/abi", "\xc7\x01\x9c\x01"},
+	{"internal/asan", "\xe3\x02"},
+	{"internal/bisect", "\xc1\x02\r\x01"},
+	{"internal/buildcfg", "\x82\x01Hj\x06\x02\x05\n\x01"},
+	{"internal/bytealg", "\xbf\x01\xa4\x01"},
 	{"internal/byteorder", ""},
 	{"internal/cfg", ""},
-	{"internal/cgrouptest", "\x81\x01\\V\x06\x0f\x02\x01\x04\x01"},
-	{"internal/chacha8rand", "\xa9\x01\x15\b\x9c\x01"},
+	{"internal/cgrouptest", "\x82\x01\\V\x06\x0f\x02\x01\x04\x01"},
+	{"internal/chacha8rand", "\xaa\x01\x15\b\x9c\x01"},
 	{"internal/copyright", ""},
 	{"internal/coverage", ""},
 	{"internal/coverage/calloc", ""},
-	{"internal/coverage/cfile", "t\r\x16\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01$\x02'.\x06\a\n\x01\x03\x0e\x06"},
-	{"internal/coverage/cformat", "\x04}-\x04S\v8\x01\x02\x0e"},
-	{"internal/coverage/cmerge", "\x81\x01-c"},
-	{"internal/coverage/decodecounter", "p\x11-\v\x02J.\x17\x18"},
-	{"internal/coverage/decodemeta", "\x02n\x11\x16\x17\v\x02J."},
-	{"internal/coverage/encodecounter", "\x02n\x11-\f\x01\x02H\v#\x15"},
-	{"internal/coverage/encodemeta", "\x02\x01m\x11\x12\x04\x17\r\x02H./"},
-	{"internal/coverage/pods", "\x04}-\x85\x01\x06\x05\n\x02\x01"},
-	{"internal/coverage/rtcov", "\xe2\x02"},
-	{"internal/coverage/slicereader", "p\x11\x84\x01]"},
-	{"internal/coverage/slicewriter", "\x81\x01\x84\x01"},
-	{"internal/coverage/stringtab", "\x81\x018\x04H"},
+	{"internal/coverage/cfile", "u\r\x16\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01$\x02'.\x06\a\n\x01\x03\x0e\x06"},
+	{"internal/coverage/cformat", "\x04~-\x04S\v8\x01\x02\x0e"},
+	{"internal/coverage/cmerge", "\x82\x01-c"},
+	{"internal/coverage/decodecounter", "q\x11-\v\x02J.\x17\x18"},
+	{"internal/coverage/decodemeta", "\x02o\x11\x16\x17\v\x02J."},
+	{"internal/coverage/encodecounter", "\x02o\x11-\f\x01\x02H\v#\x15"},
+	{"internal/coverage/encodemeta", "\x02\x01n\x11\x12\x04\x17\r\x02H./"},
+	{"internal/coverage/pods", "\x04~-\x85\x01\x06\x05\n\x02\x01"},
+	{"internal/coverage/rtcov", "\xe3\x02"},
+	{"internal/coverage/slicereader", "q\x11\x84\x01]"},
+	{"internal/coverage/slicewriter", "\x82\x01\x84\x01"},
+	{"internal/coverage/stringtab", "\x82\x018\x04H"},
 	{"internal/coverage/test", ""},
 	{"internal/coverage/uleb128", ""},
-	{"internal/cpu", "\xe2\x02"},
-	{"internal/dag", "\x04}\xc7\x01\x03"},
-	{"internal/diff", "\x03~\xc8\x01\x02"},
-	{"internal/exportdata", "\x02\x01{\x03\x02f\x1c.\x01\x05\x11\x01\x02"},
-	{"internal/filepathlite", "~*D\x1aB"},
-	{"internal/fmtsort", "\x04\xb7\x02\r"},
-	{"internal/fuzz", "\x03\nJ\x19\x04\n\x03\x01\v\x037>\f\x03\x1f\x01\x05\x02\x05\n\x01\x02\x01\x01\r\x04\x02"},
+	{"internal/cpu", "\xe3\x02"},
+	{"internal/dag", "\x04~\xc7\x01\x03"},
+	{"internal/diff", "\x03\x7f\xc8\x01\x02"},
+	{"internal/exportdata", "\x02\x01|\x03\x02f\x1c.\x01\x05\x11\x01\x02"},
+	{"internal/filepathlite", "\x7f*D\x1aB"},
+	{"internal/fmtsort", "\x04\xb8\x02\r"},
+	{"internal/fuzz", "\x03\nK\x19\x04\n\x03\x01\v\x037>\f\x03\x1f\x01\x05\x02\x05\n\x01\x02\x01\x01\r\x04\x02"},
 	{"internal/gate", "\r"},
 	{"internal/goarch", ""},
-	{"internal/godebug", "\xa6\x01\"\x85\x01\x01\x14"},
+	{"internal/godebug", "\xa7\x01\"\x85\x01\x01\x14"},
 	{"internal/godebugs", ""},
 	{"internal/goexperiment", ""},
 	{"internal/goos", ""},
-	{"internal/goroot", "\xb3\x02\x01\x05\x12\x02"},
+	{"internal/goroot", "\xb4\x02\x01\x05\x12\x02"},
 	{"internal/gover", "\x04"},
 	{"internal/goversion", ""},
-	{"internal/lazyregexp", "\xb3\x02\v\r\x02"},
-	{"internal/lazytemplate", "\x85\x02.\x18\x02\r"},
-	{"internal/msan", "\xe2\x02"},
-	{"internal/nettest", "\x03\nqG@\f\n\x12\x06\x15\x05\x0f"},
+	{"internal/lazyregexp", "\xb4\x02\v\r\x02"},
+	{"internal/lazytemplate", "\x86\x02.\x18\x02\r"},
+	{"internal/msan", "\xe3\x02"},
+	{"internal/nettest", "\x03\nrG@\f\n\x12\x06\x15\x05\x0f"},
 	{"internal/nettrace", ""},
-	{"internal/obscuretestdata", "o\x96\x01."},
-	{"internal/oserror", "~"},
-	{"internal/pkgbits", "\x03T\x19\x0e\x03\x04\vu\r!\r\n\x01"},
+	{"internal/obscuretestdata", "p\x96\x01."},
+	{"internal/oserror", "\x7f"},
+	{"internal/pkgbits", "\x03U\x19\x0e\x03\x04\vu\r!\r\n\x01"},
 	{"internal/platform", ""},
-	{"internal/poll", "~m\x05\x15;\r\x01\x01\r\x06"},
-	{"internal/profile", "\x03\x04w\x03\x84\x019\n\x01\x01\x01\x11"},
+	{"internal/poll", "\x7fm\x05\x15;\r\x01\x01\r\x06"},
+	{"internal/profile", "\x03\x04x\x03\x84\x019\n\x01\x01\x01\x11"},
 	{"internal/profilerecord", ""},
-	{"internal/race", "\xa4\x01\xbe\x01"},
-	{"internal/reflectlite", "\xa4\x01\"<>\""},
-	{"internal/runtime/atomic", "\xc6\x01\x9c\x01"},
-	{"internal/runtime/cgroup", "\xa8\x01?\x04w"},
-	{"internal/runtime/exithook", "\xdc\x01\x86\x01"},
-	{"internal/runtime/gc", "\xc6\x01"},
-	{"internal/runtime/gc/internal/gen", "\nf\x11\x17m\x04\v\x1f\b\x10\x02"},
-	{"internal/runtime/gc/scan", "\xbe\x01\b\x19\a|"},
-	{"internal/runtime/maps", "\xa4\x01\x01\x04\x15\b\x03\a\n\t\x03.N"},
-	{"internal/runtime/math", "\xc6\x01"},
+	{"internal/race", "\xa5\x01\xbe\x01"},
+	{"internal/reflectlite", "\xa5\x01\"<>\""},
+	{"internal/runtime/atomic", "\xc7\x01\x9c\x01"},
+	{"internal/runtime/cgroup", "\xa9\x01?\x04w"},
+	{"internal/runtime/exithook", "\xdd\x01\x86\x01"},
+	{"internal/runtime/gc", "\xc7\x01"},
+	{"internal/runtime/gc/internal/gen", "\ng\x11\x17m\x04\v\x1f\b\x10\x02"},
+	{"internal/runtime/gc/scan", "\xbf\x01\b\x19\a|"},
+	{"internal/runtime/maps", "\xa5\x01\x01\x04\x15\b\x03\a\n\t\x03.N"},
+	{"internal/runtime/math", "\xc7\x01"},
 	{"internal/runtime/pprof/label", ""},
 	{"internal/runtime/startlinetest", ""},
-	{"internal/runtime/sys", "\xc6\x01\x04"},
-	{"internal/runtime/syscall/linux", "\xc6\x01\x9c\x01"},
+	{"internal/runtime/sys", "\xc7\x01\x04"},
+	{"internal/runtime/syscall/linux", "\xc7\x01\x9c\x01"},
 	{"internal/runtime/wasitest", ""},
-	{"internal/saferio", "\x85\x02]"},
-	{"internal/singleflight", "\xcd\x02"},
-	{"internal/strconv", "\x94\x02N"},
-	{"internal/stringslite", "\xa8\x01\xba\x01"},
-	{"internal/sync", "\xa4\x01\"\x14t\x14"},
-	{"internal/synctest", "\xa4\x01\xbe\x01"},
-	{"internal/syscall/execenv", "\xcf\x02"},
-	{"internal/syscall/unix", "\xeb\x01U\x0e\x01\x13"},
-	{"internal/sysinfo", "\x02\x01\xbb\x01G.\x18\x02"},
+	{"internal/saferio", "\x86\x02]"},
+	{"internal/singleflight", "\xce\x02"},
+	{"internal/strconv", "\x95\x02N"},
+	{"internal/stringslite", "\xa9\x01\xba\x01"},
+	{"internal/sync", "\xa5\x01\"\x14t\x14"},
+	{"internal/synctest", "\xa5\x01\xbe\x01"},
+	{"internal/syscall/execenv", "\xd0\x02"},
+	{"internal/syscall/unix", "\xec\x01U\x0e\x01\x13"},
+	{"internal/sysinfo", "\x02\x01\xbc\x01G.\x18\x02"},
 	{"internal/syslist", ""},
-	{"internal/testenv", "\x03\nq\x02\x01)\x1c\x100-\x01\x05\a\n\x01\x02\x02\x01\f"},
-	{"internal/testhash", "\x03\x90\x01r\x11:\f"},
-	{"internal/testlog", "\xcd\x02\x01\x14"},
-	{"internal/testpty", "~\x03\xb2\x01"},
-	{"internal/trace", "\x02\x01\x01\x06f\x0e\x03x\x03\x03\x06\x03\t-\n\x01\x01\x01\x11\x06"},
-	{"internal/trace/internal/testgen", "\x03m\x11v\x03\x02\x03\x013\v\r\x11"},
-	{"internal/trace/internal/tracev1", "\x03\x01l\x0e\x03~\x06\f7\x01"},
-	{"internal/trace/raw", "\x02n\x11{\x03\x06E\x01\x13"},
-	{"internal/trace/testtrace", "\x02\x01{\x03r\x04\x03\x05\x01\x05.\v\x02\b\x02\x01\x05"},
+	{"internal/testenv", "\x03\nr\x02\x01)\x1c\x100-\x01\x05\a\n\x01\x02\x02\x01\f"},
+	{"internal/testhash", "\x03\x91\x01r\x11:\f"},
+	{"internal/testlog", "\xce\x02\x01\x14"},
+	{"internal/testpty", "\x7f\x03\xb2\x01"},
+	{"internal/trace", "\x02\x01\x01\x06g\x0e\x03x\x03\x03\x06\x03\t-\n\x01\x01\x01\x11\x06"},
+	{"internal/trace/internal/testgen", "\x03n\x11v\x03\x02\x03\x013\v\r\x11"},
+	{"internal/trace/internal/tracev1", "\x03\x01m\x0e\x03~\x06\f7\x01"},
+	{"internal/trace/raw", "\x02o\x11{\x03\x06E\x01\x13"},
+	{"internal/trace/testtrace", "\x02\x01|\x03r\x04\x03\x05\x01\x05.\v\x02\b\x02\x01\x05"},
 	{"internal/trace/tracev2", ""},
-	{"internal/trace/traceviewer", "\x02g\v\r\x19>\x1f\a\a\x04\b\v\x17\x01\x05\a\n\x01\x02\x0f"},
+	{"internal/trace/traceviewer", "\x02h\v\r\x19>\x1f\a\a\x04\b\v\x17\x01\x05\a\n\x01\x02\x0f"},
 	{"internal/trace/traceviewer/format", ""},
-	{"internal/trace/version", "\x81\x01{\t"},
-	{"internal/txtar", "\x03~\xb2\x01\x18"},
-	{"internal/types/errors", "\xca\x02"},
-	{"internal/unsafeheader", "\xe2\x02"},
-	{"internal/xcoff", "c\r\x0e\x03h\x1c.\x17\x01"},
-	{"internal/zstd", "p\x0e\x03\x84\x01\x0f"},
-	{"io", "~\xcf\x01"},
-	{"io/fs", "~*,13\x10\x14\x04"},
-	{"io/ioutil", "\x85\x02\x01-\x15\x03"},
-	{"iter", "\xda\x01f\""},
-	{"log", "\x81\x01\x84\x01\x05)\r\r\x01\x0e"},
+	{"internal/trace/version", "\x82\x01{\t"},
+	{"internal/txtar", "\x03\x7f\xb2\x01\x18"},
+	{"internal/types/errors", "\xcb\x02"},
+	{"internal/unsafeheader", "\xe3\x02"},
+	{"internal/xcoff", "d\r\x0e\x03h\x1c.\x17\x01"},
+	{"internal/zstd", "q\x0e\x03\x84\x01\x0f"},
+	{"io", "\x7f\xcf\x01"},
+	{"io/fs", "\x7f*,13\x10\x14\x04"},
+	{"io/ioutil", "\x86\x02\x01-\x15\x03"},
+	{"iter", "\xdb\x01f\""},
+	{"log", "\x82\x01\x84\x01\x05)\r\r\x01\x0e"},
 	{"log/internal", ""},
-	{"log/slog", "\x03\n^\t\n\x03H<\x04\x01\x02\x02\x03*\x05\b\x02\x01\x02\x01\x0e\x02\x02\x02"},
+	{"log/slog", "\x03\n_\t\n\x03H<\x04\x01\x02\x02\x03*\x05\b\x02\x01\x02\x01\x0e\x02\x02\x02"},
 	{"log/slog/internal", ""},
-	{"log/slog/internal/benchmarks", "\rq\x03\x84\x01\x06\x03<\x12"},
-	{"log/slog/internal/buffer", "\xcd\x02"},
-	{"log/syslog", "~\x03\x88\x01\x12\x18\x18\x02\x0f"},
-	{"maps", "\x88\x02Z"},
-	{"math", "\xbe\x01VN"},
-	{"math/big", "\x03{\x03(\x15G\f\x03\x022\x02\x01\x02\x15"},
-	{"math/big/internal/asmgen", "\x03\x01}\x93\x014\x03"},
-	{"math/bits", "\xe2\x02"},
-	{"math/cmplx", "\x91\x02\x03"},
-	{"math/rand", "\xc7\x01J<\x01\x14"},
-	{"math/rand/v2", "~+\x03e\x03N"},
-	{"mime", "\x02\x01l\x0f\x03\x84\x01\v#\x15\x03\x02\x11\x02"},
-	{"mime/multipart", "\x02\x01P+\x03F>\v\x01\a\x02\x17\x02\x06\x0f\x02\x01\x17"},
-	{"mime/quotedprintable", "\x02\x01~\x84\x01"},
-	{"net", "\x04\tq*\x1f\v\x05\x13\x01\x01\x04\x15\x01'\x06\r\b\x05\x01\x01\r\x06\t"},
-	{"net/http", "\x02\x01\x03\x01\x04\x02N\x14\x0f\x03F>\x01\x03\a\x01\x06\x01\x01\x02\x06\x02\x01\x01\f\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\x0e\x02\x02\x02\n\x01\x03"},
-	{"net/http/cgi", "\x02Y#\x03\x84\x01\x04\a\v\x01\x15\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x11\x10"},
-	{"net/http/cookiejar", "\x04z\x03\x9a\x01\x01\b\t\x05\x16\x03\x02\x0f\x04"},
-	{"net/http/fcgi", "\x02\x01\nc\x0e\x03\x84\x01\x16\x01\x01\x16\x18\x02\x0f"},
-	{"net/http/httptest", "\x02\x01\nN\x02#\x01P4\x04\x12\x01\f\t\x02\r\n\x01\x02\x03\f\x06\n"},
-	{"net/http/httptrace", "\rNwI\x16+"},
-	{"net/http/httputil", "\x02\x01\nq\x03F>\x04\x0f\x03\x01\x05\x02\x01\r\x01\x19\x02\x01\x0e\x10"},
-	{"net/http/internal", "\x02\x01m\x0e\x03\x84\x01"},
-	{"net/http/internal/ascii", "\xcb\x02\x13"},
-	{"net/http/internal/http2", "\x02\x01\x03\x01\x06F\b\x15\x0e\x03\x84\x01\x01\x03\b\x03\x02\x03\x02\x06\x02\x03\x01\n\x01\x01\b\x05\b\x02\x01\x02\x01\x0e\x10\x02\x02"},
-	{"net/http/internal/httpcommon", "\rq\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
-	{"net/http/internal/httpsfv", "\xc8\x02\x02\x01\x11\x04"},
-	{"net/http/internal/testcert", "\xcb\x02"},
-	{"net/http/pprof", "\x02\x01\nt\x18.\x11-\x04\x13\x16\x01\r\x04\x03\x01\x02\x01\x11"},
+	{"log/slog/internal/benchmarks", "\rr\x03\x84\x01\x06\x03<\x12"},
+	{"log/slog/internal/buffer", "\xce\x02"},
+	{"log/syslog", "\x7f\x03\x88\x01\x12\x18\x18\x02\x0f"},
+	{"maps", "\x89\x02Z"},
+	{"math", "\xbf\x01VN"},
+	{"math/big", "\x03|\x03(\x15G\f\x03\x022\x02\x01\x02\x15"},
+	{"math/big/internal/asmgen", "\x03\x01~\x93\x014\x03"},
+	{"math/bits", "\xe3\x02"},
+	{"math/cmplx", "\x92\x02\x03"},
+	{"math/rand", "\xc8\x01J<\x01\x14"},
+	{"math/rand/v2", "\x7f+\x03e\x03N"},
+	{"mime", "\x02\x01m\x0f\x03\x84\x01\v#\x15\x03\x02\x11\x02"},
+	{"mime/multipart", "\x02\x01Q+\x03F>\v\x01\a\x02\x17\x02\x06\x0f\x02\x01\x17"},
+	{"mime/quotedprintable", "\x02\x01\x7f\x84\x01"},
+	{"net", "\x04\tr*\x1f\v\x05\x13\x01\x01\x04\x15\x01'\x06\r\b\x05\x01\x01\r\x06\t"},
+	{"net/http", "\x02\x01\x03\x01\x04\x02O\x14\x0f\x03F>\x01\x03\a\x01\x06\x01\x01\x02\x06\x02\x01\x01\f\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\x0e\x02\x02\x02\n\x01\x03"},
+	{"net/http/cgi", "\x02Z#\x03\x84\x01\x04\a\v\x01\x15\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x11\x10"},
+	{"net/http/cookiejar", "\x04{\x03\x9a\x01\x01\b\t\x05\x16\x03\x02\x0f\x04"},
+	{"net/http/fcgi", "\x02\x01\nd\x0e\x03\x84\x01\x16\x01\x01\x16\x18\x02\x0f"},
+	{"net/http/httptest", "\x02\x01\nO\x02#\x01P4\x04\x12\x01\f\t\x02\r\n\x01\x02\x03\f\x06\n"},
+	{"net/http/httptrace", "\rOwI\x16+"},
+	{"net/http/httputil", "\x02\x01\nr\x03F>\x04\x0f\x03\x01\x05\x02\x01\r\x01\x19\x02\x0f\x10"},
+	{"net/http/internal", "\x02\x01n\x0e\x03\x84\x01"},
+	{"net/http/internal/ascii", "\xcc\x02\x13"},
+	{"net/http/internal/http2", "\x02\x01\x03\x01\x06G\b\x15\x0e\x03\x84\x01\x01\x03\b\x03\x02\x03\x02\x06\x02\x03\x01\n\x01\x01\b\x05\b\x02\x01\x02\x01\x0e\x10\x02\x02"},
+	{"net/http/internal/httpcommon", "\rr\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
+	{"net/http/internal/httpsfv", "\xc9\x02\x02\x01\x11\x04"},
+	{"net/http/internal/testcert", "\xcc\x02"},
+	{"net/http/pprof", "\x02\x01\nu\x18.\x11-\x04\x13\x16\x01\r\x04\x03\x01\x02\x01\x11"},
 	{"net/internal/cgotest", ""},
-	{"net/internal/socktest", "\x81\x01\xcc\x01\x02"},
-	{"net/mail", "\x02|\x03\x84\x01\x04\x0f\x03\x16\x1a\x02\x0f\x04"},
-	{"net/netip", "\x04z*\x01h\x036\x17"},
-	{"net/rpc", "\x02p\f\x03\x0f\nk\x04\x12\x01\x1f\r\x03\x02"},
-	{"net/rpc/jsonrpc", "t\n\x03\x84\x01\x16\x13\x1f"},
-	{"net/smtp", "\x195\r\x14\x0f\x03\x84\x01\x16\x16\x1a"},
-	{"net/textproto", "\x02\x01{\x03\x84\x01\f\n/\x01\x02\x15"},
-	{"net/url", "~\x03Ff\v\x10\x02\x01\x17"},
-	{"os", "~*\x01\x19\x04\x11\x14\x01\x03\x01\x05\x10\x01:\b\x05\x01\x01\r\x06"},
-	{"os/exec", "\x03\nqI(\x01\x15\x01-\x06\a\n\x01\x03\x01\r"},
-	{"os/exec/internal/fdtest", "\xcf\x02"},
-	{"os/signal", "\r\xa6\x02\x15\x05\x02"},
-	{"os/user", "\x02\x01{\x03\x84\x01.\r\n\x01\x02"},
-	{"path", "~*\xb8\x01"},
-	{"path/filepath", "~*\x1aD-\r\b\x03\x04\x11"},
-	{"plugin", "~"},
-	{"reflect", "~&\x04\x1e\x03\x11\b\x04\x05\x17\x06\t/\n\x03\x11\x02\x02"},
+	{"net/internal/socktest", "\x82\x01\xcc\x01\x02"},
+	{"net/mail", "\x02}\x03\x84\x01\x04\x0f\x03\x16\x1a\x02\x0f\x04"},
+	{"net/netip", "\x04{*\x01h\x036\x17"},
+	{"net/rpc", "\x02q\f\x03\x0f\nk\x04\x12\x01\x1f\r\x03\x02"},
+	{"net/rpc/jsonrpc", "u\n\x03\x84\x01\x16\x13\x1f"},
+	{"net/smtp", "\x196\r\x14\x0f\x03\x84\x01\x16\x16\x1a"},
+	{"net/textproto", "\x02\x01|\x03\x84\x01\f\n/\x01\x02\x15"},
+	{"net/url", "\x7f\x03Ff\v\x10\x02\x01\x17"},
+	{"os", "\x7f*\x01\x19\x04\x11\x14\x01\x03\x01\x05\x10\x01:\b\x05\x01\x01\r\x06"},
+	{"os/exec", "\x03\nrI(\x01\x15\x01-\x06\a\n\x01\x03\x01\r"},
+	{"os/exec/internal/fdtest", "\xd0\x02"},
+	{"os/signal", "\r\xa7\x02\x15\x05\x02"},
+	{"os/user", "\x02\x01|\x03\x84\x01.\r\n\x01\x02"},
+	{"path", "\x7f*\xb8\x01"},
+	{"path/filepath", "\x7f*\x1aD-\r\b\x03\x04\x11"},
+	{"plugin", "\x7f"},
+	{"reflect", "\x7f&\x04\x1e\x03\x11\b\x04\x05\x17\x06\t/\n\x03\x11\x02\x02"},
 	{"reflect/internal/example1", ""},
 	{"reflect/internal/example2", ""},
-	{"regexp", "\x03\x82\x02\x037\t\x02\x01\x02\x11\x02"},
-	{"regexp/syntax", "\xc8\x02\x01\x01\x01\x02\x11\x02"},
-	{"runtime", "\xa4\x01\x04\x01\x03\f\x06\b\x02\x01\x01\x0f\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18N"},
-	{"runtime/coverage", "\xb0\x01U"},
-	{"runtime/debug", "\x81\x01V\\\r\b\x02\x01\x11\x06"},
-	{"runtime/metrics", "\xc8\x01I/\""},
-	{"runtime/pprof", "\x02\x01\x01\x03\x06c\x0e\x03#5\v!\f\"\r\b\x01\x01\x01\x02\x02\n\x03\x06"},
-	{"runtime/race", "\xc6\x02"},
+	{"regexp", "\x03\x83\x02\x037\t\x02\x01\x02\x11\x02"},
+	{"regexp/syntax", "\xc9\x02\x01\x01\x01\x02\x11\x02"},
+	{"runtime", "\xa5\x01\x04\x01\x03\f\x06\b\x02\x01\x01\x0f\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18N"},
+	{"runtime/coverage", "\xb1\x01U"},
+	{"runtime/debug", "\x82\x01V\\\r\b\x02\x01\x11\x06"},
+	{"runtime/metrics", "\xc9\x01I/\""},
+	{"runtime/pprof", "\x02\x01\x01\x03\x06d\x0e\x03#5\v!\f\"\r\b\x01\x01\x01\x02\x02\n\x03\x06"},
+	{"runtime/race", "\xc7\x02"},
 	{"runtime/race/internal/amd64v1", ""},
-	{"runtime/trace", "\rq\x03{\t;\b\x05\x01\x0e\x06"},
-	{"slices", "\x04\x84\x02\fN"},
-	{"sort", "\xdb\x0194"},
-	{"strconv", "~*C\x01t"},
-	{"strings", "~&\x04D\x19\x03\f9\x11\x02\x02"},
+	{"runtime/trace", "\rr\x03{\t;\b\x05\x01\x0e\x06"},
+	{"slices", "\x04\x85\x02\fN"},
+	{"sort", "\xdc\x0194"},
+	{"strconv", "\x7f*C\x01t"},
+	{"strings", "\x7f&\x04D\x19\x03\f9\x11\x02\x02"},
 	{"structs", ""},
-	{"sync", "\xda\x01\x02\x11\x01R\x0e\x14"},
-	{"sync/atomic", "\xe2\x02"},
-	{"syscall", "~'\x03\x01\x1d\n\x04\x06\r\x04U\b\x05\x01\x14"},
-	{"testing", "\x03\nq\x02\x01Y\x17\x14\f\x05\x1d\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\x0e\x02\x04"},
-	{"testing/cryptotest", "SV\\\x126\x03\x12"},
-	{"testing/fstest", "~\x03\x84\x01\x01\n(\x10\x03\t\b"},
-	{"testing/internal/testdeps", "\x02\v\xb7\x011\x10.\x03\x05\x03\x06\a\x02\x0f"},
-	{"testing/iotest", "\x03{\x03\x84\x01\x04"},
-	{"testing/quick", "\x80\x01\x01\x90\x01\x05%\x10\x11"},
-	{"testing/slogtest", "\rq\x03\x8a\x010\x05\x10\f"},
-	{"testing/synctest", "\xee\x01b\f\x06"},
-	{"text/scanner", "\x03~\x84\x01.+\x02"},
-	{"text/tabwriter", "\x81\x01\x84\x01["},
-	{"text/template", "~\x03BB\x01\n\"\x01\x05\x01\x02\x05\v\x02\x0e\x03\x02"},
-	{"text/template/parse", "\x03~\xbf\x01\n\x01\x13\x02"},
-	{"time", "~*D(,\r\x02\x13"},
-	{"time/tzdata", "~\xd1\x01\x13"},
+	{"sync", "\xdb\x01\x02\x11\x01R\x0e\x14"},
+	{"sync/atomic", "\xe3\x02"},
+	{"syscall", "\x7f'\x03\x01\x1d\n\x04\x06\r\x04U\b\x05\x01\x14"},
+	{"testing", "\x03\nr\x02\x01Y\x17\x14\f\x05\x1d\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\x0e\x02\x04"},
+	{"testing/cryptotest", "TV\\\x126\x03\x12"},
+	{"testing/fstest", "\x7f\x03\x84\x01\x01\n(\x10\x03\t\b"},
+	{"testing/internal/testdeps", "\x02\v\xb8\x011\x10.\x03\x05\x03\x06\a\x02\x0f"},
+	{"testing/iotest", "\x03|\x03\x84\x01\x04"},
+	{"testing/quick", "\x81\x01\x01\x90\x01\x05%\x10\x11"},
+	{"testing/slogtest", "\rr\x03\x8a\x010\x05\x10\f"},
+	{"testing/synctest", "\xef\x01b\f\x06"},
+	{"text/scanner", "\x03\x7f\x84\x01.+\x02"},
+	{"text/tabwriter", "\x82\x01\x84\x01["},
+	{"text/template", "\x7f\x03BB\x01\n\"\x01\x05\x01\x02\x05\v\x02\x0e\x03\x02"},
+	{"text/template/parse", "\x03\x7f\xbf\x01\n\x01\x13\x02"},
+	{"time", "\x7f*D(,\r\x02\x13"},
+	{"time/tzdata", "\x7f\xd1\x01\x13"},
 	{"unicode", ""},
 	{"unicode/utf16", ""},
 	{"unicode/utf8", ""},
-	{"unique", "\xa4\x01\"&\x01S\r\x01\x14\x19"},
+	{"unique", "\xa5\x01\"&\x01S\r\x01\x14\x19"},
 	{"unsafe", ""},
-	{"uuid", "\x03\x01O\x1d\x03\v\xcf\x01\x0f"},
-	{"vendor/golang.org/x/crypto/chacha20", "\x10`\x0e\x96\x01,)"},
-	{"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\aY\x0e\xe6\x01\x05\x01\f"},
-	{"vendor/golang.org/x/crypto/cryptobyte", "m\x11\x03\x91\x01)!\v"},
+	{"uuid", "\x03\x01P\x1d\x03\v\xcf\x01\x0f"},
+	{"vendor/golang.org/x/crypto/chacha20", "\x10a\x0e\x96\x01,)"},
+	{"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\aZ\x0e\xe6\x01\x05\x01\f"},
+	{"vendor/golang.org/x/crypto/cryptobyte", "n\x11\x03\x91\x01)!\v"},
 	{"vendor/golang.org/x/crypto/cryptobyte/asn1", ""},
-	{"vendor/golang.org/x/crypto/hkdf", "\x18\x01e\x15r"},
-	{"vendor/golang.org/x/crypto/internal/alias", "\xe2\x02"},
-	{"vendor/golang.org/x/crypto/internal/poly1305", "Z\x16\xa4\x01"},
-	{"vendor/golang.org/x/net/dns/dnsmessage", "~\xca\x01"},
-	{"vendor/golang.org/x/net/http/httpguts", "\x9b\x02\x16\x1a\x15\x10"},
-	{"vendor/golang.org/x/net/http/httpproxy", "~\x03\x9a\x01\x12\x05\x01\x18\x15\x10"},
-	{"vendor/golang.org/x/net/http2/hpack", "\x03{\x03\x84\x01H"},
-	{"vendor/golang.org/x/net/http3", "\x9c\x02@\x06\x0f\x04"},
-	{"vendor/golang.org/x/net/idna", "\x81\x01\x90\x01:\x13\x02\x17\x02\x01"},
-	{"vendor/golang.org/x/net/internal/http3", "\rN#\x03\x84\x01\v\x04\a\x01\x05\x10\x01\x16\x02\x01\x02\x0f\x10\x02\x04\x03"},
-	{"vendor/golang.org/x/net/internal/httpcommon", "\rq\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
-	{"vendor/golang.org/x/net/internal/quic/quicwire", "p"},
-	{"vendor/golang.org/x/net/nettest", "\x03m\x0e\x03\x84\x01\x11\x05\x18\x01\f\n\x01\x02\x02\x01\f"},
-	{"vendor/golang.org/x/net/quic", "\x03\n\x01\x01\x01\t:\x04\x04\x15\x03\v\x03\x12r\x06\x06\x06\x04\x12\x06\x15\x02\x01\x02\x01\x01\r\x06\x02\x01\x01\x02\v"},
-	{"vendor/golang.org/x/sys/cpu", "\xb3\x02\r\n\x01\x17"},
-	{"vendor/golang.org/x/text/secure/bidirule", "~\xe2\x01\x18\x01"},
-	{"vendor/golang.org/x/text/transform", "\x03{\x87\x01["},
-	{"vendor/golang.org/x/text/unicode/bidi", "\x03\bv\x88\x01@\x17"},
-	{"vendor/golang.org/x/text/unicode/norm", "p\x11\x84\x01H\x13\x18"},
-	{"weak", "\xa4\x01\x9c\x01\""},
+	{"vendor/golang.org/x/crypto/hkdf", "\x18\x01f\x15r"},
+	{"vendor/golang.org/x/crypto/internal/alias", "\xe3\x02"},
+	{"vendor/golang.org/x/crypto/internal/poly1305", "[\x16\xa4\x01"},
+	{"vendor/golang.org/x/net/dns/dnsmessage", "\x7f\xca\x01"},
+	{"vendor/golang.org/x/net/http/httpguts", "\x9c\x02\x16\x1a\x15\x10"},
+	{"vendor/golang.org/x/net/http/httpproxy", "\x7f\x03\x9a\x01\x12\x05\x01\x18\x15\x10"},
+	{"vendor/golang.org/x/net/http2/hpack", "\x03|\x03\x84\x01H"},
+	{"vendor/golang.org/x/net/http3", "\x9d\x02@\x06\x0f\x04"},
+	{"vendor/golang.org/x/net/idna", "\x82\x01\x90\x01:\x13\x02\x17\x02\x01"},
+	{"vendor/golang.org/x/net/internal/http3", "\rO#\x03\x84\x01\v\x04\a\x01\x05\x10\x01\x16\x02\x01\x02\x0f\x10\x02\x04\x03"},
+	{"vendor/golang.org/x/net/internal/httpcommon", "\rr\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
+	{"vendor/golang.org/x/net/internal/quic/quicwire", "q"},
+	{"vendor/golang.org/x/net/nettest", "\x03n\x0e\x03\x84\x01\x11\x05\x18\x01\f\n\x01\x02\x02\x01\f"},
+	{"vendor/golang.org/x/net/quic", "\x03\n\x01\x01\x01\t;\x04\x04\x15\x03\v\x03\x12r\x06\x06\x06\x04\x12\x06\x15\x02\x01\x02\x01\x01\r\x06\x02\x01\x01\x02\v"},
+	{"vendor/golang.org/x/sys/cpu", "\xb4\x02\r\n\x01\x17"},
+	{"vendor/golang.org/x/text/secure/bidirule", "\x7f\xe2\x01\x18\x01"},
+	{"vendor/golang.org/x/text/transform", "\x03|\x87\x01["},
+	{"vendor/golang.org/x/text/unicode/bidi", "\x03\bw\x88\x01@\x17"},
+	{"vendor/golang.org/x/text/unicode/norm", "q\x11\x84\x01H\x13\x18"},
+	{"weak", "\xa5\x01\x9c\x01\""},
 }
 
 // bootstrap is the list of bootstrap packages extracted from cmd/dist.
diff --git a/vendor/golang.org/x/tools/internal/stdlib/manifest.go b/vendor/golang.org/x/tools/internal/stdlib/manifest.go
index 2180c296..e11abf04 100644
--- a/vendor/golang.org/x/tools/internal/stdlib/manifest.go
+++ b/vendor/golang.org/x/tools/internal/stdlib/manifest.go
@@ -9153,6 +9153,7 @@ var PackageSymbols = map[string][]Symbol{
 		{"CrossOriginProtection", Type, 25, ""},
 		{"DefaultClient", Var, 0, ""},
 		{"DefaultMaxHeaderBytes", Const, 0, ""},
+		{"DefaultMaxHeaderValueCount", Const, 27, ""},
 		{"DefaultMaxIdleConnsPerHost", Const, 0, ""},
 		{"DefaultServeMux", Var, 0, ""},
 		{"DefaultTransport", Var, 0, ""},
@@ -9317,6 +9318,7 @@ var PackageSymbols = map[string][]Symbol{
 		{"Server.Handler", Field, 0, ""},
 		{"Server.IdleTimeout", Field, 8, ""},
 		{"Server.MaxHeaderBytes", Field, 0, ""},
+		{"Server.MaxHeaderValueCount", Field, 27, ""},
 		{"Server.Protocols", Field, 24, ""},
 		{"Server.ReadHeaderTimeout", Field, 8, ""},
 		{"Server.ReadTimeout", Field, 0, ""},
diff --git a/vendor/golang.org/x/tools/internal/typesinternal/assignedaddress.go b/vendor/golang.org/x/tools/internal/typesinternal/assignedaddress.go
new file mode 100644
index 00000000..020defc3
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/typesinternal/assignedaddress.go
@@ -0,0 +1,128 @@
+// Copyright 2026 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package typesinternal
+
+import (
+	"go/ast"
+	"go/token"
+	"go/types"
+
+	"golang.org/x/tools/go/ast/edge"
+	"golang.org/x/tools/go/ast/inspector"
+)
+
+// IsAssignedOrAddressTaken reports whether the expression cur denotes a
+// variable and appears in a context that assigns it or that takes its address,
+// potentially leading to indirect assignment.
+//
+// These examples cause IsAssignedOrAddressTaken on the identifier for x to
+// return true:
+//
+//		x = 1
+//		x++
+//		x[i] = 1	   (assume x is an array)
+//		x.a[i] = 1	 (assume x.a is a non-pointer struct field)
+//	  use(&x)
+//
+// whereas these cause it to return false:
+//
+//	y = x
+//	f(x)
+//	use(x.a[i])
+//	use(*x)
+//
+// The expression may itself be a compound, for example:
+//
+//	use(&(*ptr))  => IsAssignedOrAddressTaken("*ptr") = true
+//	x.a[i] = 1    => IsAssignedOrAddressTaken("x.a")  = true
+//	_ = x.a[i]    => IsAssignedOrAddressTaken("x.a")  = false
+//
+// A variable's declaration is not considered to be an assignment:
+//
+//	var x int     => IsAssignedOrAddressTaken(x) = false
+//	x := 1        => IsAssignedOrAddressTaken(x) = false
+//
+// TODO(adonovan): revisit the surprising behavior for declarations.
+func IsAssignedOrAddressTaken(info *types.Info, cur inspector.Cursor) bool {
+	// Unfortunately we can't simply use info.Types[e].Assignable()
+	// as it is always true for a variable even when that variable is
+	// used only as an r-value. So we must inspect enclosing syntax.
+outer:
+	// Ascend to outermost aggregate of which
+	// original cur is a part:
+	//    x -> (x) | x.f | x[i] | x[i:j]
+	for cur = range cur.Enclosing() {
+		switch cur.ParentEdgeKind() {
+		case edge.ParenExpr_X:
+			// If x is an lvalue, then (x) is an lvalue.
+		case edge.SelectorExpr_X:
+			// If x is an lvalue, then x.f is an lvalue iff
+			// the selection does not traverse a pointer.
+			sel := cur.Parent().Node().(*ast.SelectorExpr)
+			if seln, ok := info.Selections[sel]; ok {
+				// Note: there is a bug in Indirect() where it spuriously returns true
+				// when both the selection receiver and parameter are pointers. However,
+				// it's okay in this case because there is no address taken when a
+				// pointer receiver method is called on a pointer type.
+				if seln.Indirect() {
+					return false
+				}
+				if seln.Kind() == types.MethodVal {
+					sig := seln.Obj().Type().(*types.Signature)
+					if is[*types.Pointer](sig.Recv().Type().Underlying()) {
+						t := seln.Recv()
+						// The receiver may be an embedded field, so we need
+						// to get the inner-most type (right before the method
+						// call in seln.Index())
+						for _, idx := range seln.Index()[:len(seln.Index())-1] {
+							t = t.Underlying().(*types.Struct).Field(idx).Type()
+						}
+						if !is[*types.Pointer](t.Underlying()) {
+							return true // takes address of receiver
+						}
+					}
+					return false
+				}
+			}
+		case edge.IndexExpr_X, edge.SliceExpr_X:
+			// If x[i] or x[i:j] is an lvalue,
+			// then x is an lvalue iff x is an array.
+			if !is[*types.Array](info.TypeOf(cur.Node().(ast.Expr)).Underlying()) {
+				return false
+			}
+		default:
+			break outer
+		}
+	}
+	switch cur.ParentEdgeKind() {
+	case edge.AssignStmt_Lhs:
+		assign := cur.Parent().Node().(*ast.AssignStmt)
+		if assign.Tok != token.DEFINE {
+			return true // x = j or x += j
+		}
+		id := cur.Node().(*ast.Ident)
+		// Re-assigned identifiers are recorded in the Uses map.
+		if _, ok := info.Uses[id]; ok {
+			return true // reassignment of x (x, y := 1, 2)
+		}
+	case edge.RangeStmt_Key, edge.RangeStmt_Value:
+		rng := cur.Parent().Node().(*ast.RangeStmt)
+		if rng.Tok == token.ASSIGN {
+			return true // "for k, v = range x" is like an AssignStmt to k, v
+		}
+	case edge.IncDecStmt_X:
+		return true // x++, x--
+	case edge.UnaryExpr_X:
+		if cur.Parent().Node().(*ast.UnaryExpr).Op == token.AND {
+			return true // &x
+		}
+	}
+	return false
+}
+
+func is[T any](x any) bool {
+	_, ok := x.(T)
+	return ok
+}
diff --git a/vendor/golang.org/x/tools/internal/typesinternal/classify_call.go b/vendor/golang.org/x/tools/internal/typesinternal/classify_call.go
index 7ebe9768..d5c40a2e 100644
--- a/vendor/golang.org/x/tools/internal/typesinternal/classify_call.go
+++ b/vendor/golang.org/x/tools/internal/typesinternal/classify_call.go
@@ -8,7 +8,6 @@ import (
 	"fmt"
 	"go/ast"
 	"go/types"
-	_ "unsafe" // for go:linkname hack
 )
 
 // CallKind describes the function position of an [*ast.CallExpr].
@@ -72,11 +71,15 @@ func ClassifyCall(info *types.Info, call *ast.CallExpr) CallKind {
 	if tv.IsBuiltin() {
 		return CallBuiltin
 	}
-	obj := info.Uses[UsedIdent(info, call.Fun)]
+	id := UsedIdent(info, call.Fun)
+	if id == nil {
+		return CallDynamic
+	}
+	obj := info.Uses[id]
 	// Classify the call by the type of the object, if any.
 	switch obj := obj.(type) {
 	case *types.Func:
-		if interfaceMethod(obj) {
+		if isInterfaceMethod(obj) {
 			return CallInterface
 		}
 		return CallStatic
@@ -127,11 +130,69 @@ func ClassifyCall(info *types.Info, call *ast.CallExpr) CallKind {
 // Note: if e is an instantiated function or method, UsedIdent returns
 // the corresponding generic function or method on the generic type.
 func UsedIdent(info *types.Info, e ast.Expr) *ast.Ident {
-	return usedIdent(info, e)
+	if info.Types == nil || info.Uses == nil {
+		panic("one of info.Types or info.Uses is nil; both must be populated")
+	}
+	// Look through type instantiation if necessary.
+	switch d := ast.Unparen(e).(type) {
+	case *ast.IndexExpr:
+		if info.Types[d.Index].IsType() {
+			e = d.X
+		}
+	case *ast.IndexListExpr:
+		e = d.X
+	}
+
+	switch e := ast.Unparen(e).(type) {
+	// info.Uses always has the object we want, even for selector expressions.
+	// We don't need info.Selections.
+	// See go/types/recording.go:recordSelection.
+	case *ast.Ident:
+		return e
+	case *ast.SelectorExpr:
+		return e.Sel
+	}
+	return nil
+}
+
+// See [golang.org/x/tools/go/types/typeutil.Callee].
+func Callee(info *types.Info, call *ast.CallExpr) types.Object {
+	id := UsedIdent(info, call.Fun)
+	if id == nil {
+		return nil
+	}
+	obj := info.Uses[id]
+	if obj == nil {
+		return nil
+	}
+	if _, ok := obj.(*types.TypeName); ok {
+		return nil
+	}
+	if fn, ok := obj.(*types.Func); ok {
+		return fn.Origin()
+	}
+	return obj
 }
 
-//go:linkname usedIdent golang.org/x/tools/go/types/typeutil.usedIdent
-func usedIdent(info *types.Info, e ast.Expr) *ast.Ident
+// See [golang.org/x/tools/go/types/typeutil.StaticCallee].
+func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func {
+	id := UsedIdent(info, call.Fun)
+	if id == nil {
+		return nil
+	}
+	obj := info.Uses[id]
+	if obj == nil {
+		return nil
+	}
+	fn, _ := obj.(*types.Func)
+	if fn == nil || isInterfaceMethod(fn) {
+		return nil
+	}
+	return fn.Origin()
+}
 
-//go:linkname interfaceMethod golang.org/x/tools/go/types/typeutil.interfaceMethod
-func interfaceMethod(f *types.Func) bool
+// isInterfaceMethod reports whether its argument is a method of an interface.
+func isInterfaceMethod(f *types.Func) bool {
+	recv := f.Signature().Recv()
+	return recv != nil && types.IsInterface(recv.Type())
+}
diff --git a/vendor/golang.org/x/tools/internal/typesinternal/element.go b/vendor/golang.org/x/tools/internal/typesinternal/element.go
index 89eeea16..bab37fbf 100644
--- a/vendor/golang.org/x/tools/internal/typesinternal/element.go
+++ b/vendor/golang.org/x/tools/internal/typesinternal/element.go
@@ -7,8 +7,6 @@ package typesinternal
 import (
 	"fmt"
 	"go/types"
-
-	"golang.org/x/tools/go/types/typeutil"
 )
 
 // ForEachElement calls f for type T and each type reachable from its
@@ -16,25 +14,24 @@ import (
 // type constructors; in addition, for each named type N, the type *N
 // is added to the result as it may have additional methods.
 //
-// The caller must provide an initially empty set used to de-duplicate
-// identical types, potentially across multiple calls to ForEachElement.
-// (Its final value holds all the elements seen, matching the arguments
-// passed to f.)
+// The access argument passed to f indicates whether the type is
+// inaccessible to reflection (for example, intermediate tuple types
+// or underlying types of named types).
 //
-// TODO(adonovan): share/harmonize with go/callgraph/rta.
-func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T types.Type, f func(types.Type)) {
-	var visit func(T types.Type, skip bool)
-	visit = func(T types.Type, skip bool) {
-		if !skip {
-			if seen, _ := rtypes.Set(T, true).(bool); seen {
-				return // de-dup
-			}
-
-			f(T) // notify caller of new element type
+// The result of f indicates whether the caller has seen this type
+// already, so we can prune the traversal.
+//
+// methodSetOf abstracts (*typeutil.MethodSetCache).MethodSet,
+// avoiding an import cycle.
+func ForEachElement(methodSetOf func(types.Type) *types.MethodSet, T types.Type, f func(T types.Type, access bool) bool) {
+	var visit func(T types.Type, access bool)
+	visit = func(T types.Type, access bool) {
+		if f(T, access) {
+			return // duplicate; prune descent
 		}
 
 		// Recursion over signatures of each method.
-		tmset := msets.MethodSet(T)
+		tmset := methodSetOf(T)
 		for method := range tmset.Methods() {
 			sig := method.Type().(*types.Signature)
 			if sig.TypeParams() != nil {
@@ -65,13 +62,13 @@ func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T type
 			//
 			// TODO(adonovan): document whether or not it is
 			// safe to skip non-exported methods (as RTA does).
-			visit(sig.Params(), true)  // skip the Tuple
-			visit(sig.Results(), true) // skip the Tuple
+			visit(sig.Params(), false)  // the Tuple is inaccessible
+			visit(sig.Results(), false) // the Tuple is inaccessible
 		}
 
 		switch T := T.(type) {
 		case *types.Alias:
-			visit(types.Unalias(T), skip) // emulates the pre-Alias behavior
+			visit(types.Unalias(T), access) // emulates the pre-Alias behavior
 
 		case *types.Basic:
 			// nop
@@ -80,49 +77,49 @@ func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T type
 			// nop---handled by recursion over method set.
 
 		case *types.Pointer:
-			visit(T.Elem(), false)
+			visit(T.Elem(), true)
 
 		case *types.Slice:
-			visit(T.Elem(), false)
+			visit(T.Elem(), true)
 
 		case *types.Chan:
-			visit(T.Elem(), false)
+			visit(T.Elem(), true)
 
 		case *types.Map:
-			visit(T.Key(), false)
-			visit(T.Elem(), false)
+			visit(T.Key(), true)
+			visit(T.Elem(), true)
 
 		case *types.Signature:
 			if T.Recv() != nil {
 				panic(fmt.Sprintf("Signature %s has Recv %s", T, T.Recv()))
 			}
-			visit(T.Params(), true)  // skip the Tuple
-			visit(T.Results(), true) // skip the Tuple
+			visit(T.Params(), false)  // the Tuple is inaccessible
+			visit(T.Results(), false) // the Tuple is inaccessible
 
 		case *types.Named:
 			// A pointer-to-named type can be derived from a named
 			// type via reflection.  It may have methods too.
-			visit(types.NewPointer(T), false)
+			visit(types.NewPointer(T), true)
 
 			// Consider 'type T struct{S}' where S has methods.
 			// Reflection provides no way to get from T to struct{S},
 			// only to S, so the method set of struct{S} is unwanted,
-			// so set 'skip' flag during recursion.
-			visit(T.Underlying(), true) // skip the unnamed type
+			// so mark it inaccessible during recursion.
+			visit(T.Underlying(), false) // skip the unnamed type
 
 		case *types.Array:
-			visit(T.Elem(), false)
+			visit(T.Elem(), true)
 
 		case *types.Struct:
 			for i, n := 0, T.NumFields(); i < n; i++ {
 				// TODO(adonovan): document whether or not
 				// it is safe to skip non-exported fields.
-				visit(T.Field(i).Type(), false)
+				visit(T.Field(i).Type(), true)
 			}
 
 		case *types.Tuple:
 			for i, n := 0, T.Len(); i < n; i++ {
-				visit(T.At(i).Type(), false)
+				visit(T.At(i).Type(), true)
 			}
 
 		case *types.TypeParam, *types.Union:
@@ -133,5 +130,5 @@ func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T type
 			panic(fmt.Sprintf("ForEachElement called on unexpected type %T", T))
 		}
 	}
-	visit(T, false)
+	visit(T, true)
 }
diff --git a/vendor/golang.org/x/tools/internal/typesinternal/toonew.go b/vendor/golang.org/x/tools/internal/typesinternal/toonew.go
index cc86487e..386c59c7 100644
--- a/vendor/golang.org/x/tools/internal/typesinternal/toonew.go
+++ b/vendor/golang.org/x/tools/internal/typesinternal/toonew.go
@@ -13,20 +13,30 @@ import (
 
 // TooNewStdSymbols computes the set of package-level symbols
 // exported by pkg that are not available at the specified version.
-// The result maps each symbol to its minimum version.
 //
 // The pkg is allowed to contain type errors.
-func TooNewStdSymbols(pkg *types.Package, version string) map[types.Object]string {
-	disallowed := make(map[types.Object]string)
+func TooNewStdSymbols(pkg *types.Package, version string) map[types.Object]stdlib.Symbol {
+	disallowed := make(map[types.Object]stdlib.Symbol)
+
+	// Some symbols are accessible before their release but
+	// only with specific build tags unknown to us here.
+	// Avoid false positives in such cases.
+	if pkg.Path() == "testing/synctest" && versions.AtLeast(version, "go1.24") {
+		// requires go1.24 && goexperiment.synctest || go1.25
+		return disallowed
+	}
+	if (pkg.Path() == "encoding/json/v2" || pkg.Path() == "encoding/json/jsontext") && versions.AtLeast(version, "go1.25") {
+		// requires go1.25 && goexperiment.jsonv2 || go1.27
+		return disallowed
+	}
 
 	// Pass 1: package-level symbols.
 	symbols := stdlib.PackageSymbols[pkg.Path()]
 	for _, sym := range symbols {
-		symver := sym.Version.String()
-		if versions.Before(version, symver) {
+		if versions.Before(version, sym.Version.String()) {
 			switch sym.Kind {
 			case stdlib.Func, stdlib.Var, stdlib.Const, stdlib.Type:
-				disallowed[pkg.Scope().Lookup(sym.Name)] = symver
+				disallowed[pkg.Scope().Lookup(sym.Name)] = sym
 			}
 		}
 	}
@@ -60,28 +70,36 @@ func TooNewStdSymbols(pkg *types.Package, version string) map[types.Object]strin
 	// spuriously cause the analyzer to report a reference to a
 	// too-new symbol even though this expression compiles just
 	// fine (with the fake implementation) using go1.21.
+	var noSym stdlib.Symbol
+	depth := make(map[types.Object]int)
 	for _, sym := range symbols {
-		symVersion := sym.Version.String()
-		if !versions.Before(version, symVersion) {
+		if !versions.Before(version, sym.Version.String()) {
 			continue // allowed
 		}
 
 		var obj types.Object
+		var indices []int
 		switch sym.Kind {
 		case stdlib.Field:
 			typename, name := sym.SplitField()
-			if t := pkg.Scope().Lookup(typename); t != nil && disallowed[t] == "" {
-				obj, _, _ = types.LookupFieldOrMethod(t.Type(), false, pkg, name)
+			if t := pkg.Scope().Lookup(typename); t != nil && disallowed[t] == noSym {
+				obj, indices, _ = types.LookupFieldOrMethod(t.Type(), false, pkg, name)
 			}
 
 		case stdlib.Method:
 			ptr, recvname, name := sym.SplitMethod()
-			if t := pkg.Scope().Lookup(recvname); t != nil && disallowed[t] == "" {
-				obj, _, _ = types.LookupFieldOrMethod(t.Type(), ptr, pkg, name)
+			if t := pkg.Scope().Lookup(recvname); t != nil && disallowed[t] == noSym {
+				obj, indices, _ = types.LookupFieldOrMethod(t.Type(), ptr, pkg, name)
 			}
 		}
 		if obj != nil {
-			disallowed[obj] = symVersion
+			// In the presence of embedding, two or more "pkg.T.name"
+			// strings may map to the same types.Object.
+			// Prefer the Object with the shorter index path.
+			if min, ok := depth[obj]; !ok || len(indices) < min {
+				depth[obj] = len(indices)
+				disallowed[obj] = sym
+			}
 		}
 	}
 
diff --git a/vendor/golang.org/x/tools/internal/typesinternal/types.go b/vendor/golang.org/x/tools/internal/typesinternal/types.go
index d2c0b4c5..9fd48b08 100644
--- a/vendor/golang.org/x/tools/internal/typesinternal/types.go
+++ b/vendor/golang.org/x/tools/internal/typesinternal/types.go
@@ -270,3 +270,11 @@ func ImplicitFieldSelections(seln types.Selection) iter.Seq2[*types.Var, bool] {
 		}
 	}
 }
+
+func TupleOf(elems ...types.Type) *types.Tuple {
+	params := make([]*types.Var, len(elems))
+	for i, elem := range elems {
+		params[i] = types.NewParam(token.NoPos, nil, "", elem)
+	}
+	return types.NewTuple(params...)
+}
diff --git a/vendor/golang.org/x/tools/refactor/satisfy/find.go b/vendor/golang.org/x/tools/refactor/satisfy/find.go
index 720ecc17..42998687 100644
--- a/vendor/golang.org/x/tools/refactor/satisfy/find.go
+++ b/vendor/golang.org/x/tools/refactor/satisfy/find.go
@@ -11,7 +11,7 @@
 // It requires well-typed inputs, and may panic otherwise.
 //
 // This package reimplements parts of the type checker. See
-// https://go.dev/issue/70638 for a proposal to expose the the work
+// https://go.dev/issue/70638 for a proposal to expose the work
 // already done by the type checker, which would make this package
 // redundant.
 package satisfy
@@ -355,7 +355,8 @@ func (f *Finder) expr(e ast.Expr) types.Type {
 		if e.Name == "_" { // e.g. "for _ = range x"
 			return tInvalid
 		}
-		panic("undefined ident: " + e.Name)
+		// There could be a missing definition, return an invalid type
+		return tInvalid
 
 	case *ast.Ellipsis:
 		if e.Elt != nil {
@@ -374,7 +375,14 @@ func (f *Finder) expr(e ast.Expr) types.Type {
 			case *types.Struct:
 				for i, elem := range e.Elts {
 					if kv, ok := elem.(*ast.KeyValueExpr); ok {
-						f.assign(f.info.Uses[kv.Key.(*ast.Ident)].Type(), f.expr(kv.Value))
+						// in weird code, kv.Key might not be an identifier
+						id, ok := kv.Key.(*ast.Ident)
+						if !ok || f.info.Uses[id] == nil {
+							f.expr(kv.Value)
+							continue
+
+						}
+						f.assign(f.info.Uses[id].Type(), f.expr(kv.Value))
 					} else {
 						f.assign(T.Field(i).Type(), f.expr(elem))
 					}
@@ -412,7 +420,10 @@ func (f *Finder) expr(e ast.Expr) types.Type {
 				f.expr(e.X)
 			}
 		} else {
-			return f.info.Uses[e.Sel].Type() // qualified identifier
+			if obj, ok := f.info.Uses[e.Sel]; ok {
+				return obj.Type() // qualified identifier
+			}
+			return tInvalid
 		}
 
 	case *ast.IndexExpr:
diff --git a/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go b/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go
index 669133d0..c96e4483 100644
--- a/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go
+++ b/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go
@@ -32,7 +32,7 @@ var byteType = reflect.TypeOf(byte(0))
 func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescriptors) protoreflect.FieldDescriptor {
 	f := new(filedesc.Field)
 	f.L0.ParentFile = filedesc.SurrogateProto2
-	f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures
+	packed := false
 	for len(tag) > 0 {
 		i := strings.IndexByte(tag, ',')
 		if i < 0 {
@@ -108,7 +108,7 @@ func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescri
 				f.L1.StringName.InitJSON(jsonName)
 			}
 		case s == "packed":
-			f.L1.EditionFeatures.IsPacked = true
+			packed = true
 		case strings.HasPrefix(s, "def="):
 			// The default tag is special in that everything afterwards is the
 			// default regardless of the presence of commas.
@@ -121,6 +121,13 @@ func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescri
 		tag = strings.TrimPrefix(tag[i:], ",")
 	}
 
+	// Update EditionFeatures after the loop and after we know whether this is
+	// a proto2 or proto3 field.
+	f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures
+	if packed {
+		f.L1.EditionFeatures.IsPacked = true
+	}
+
 	// The generator uses the group message name instead of the field name.
 	// We obtain the real field name by lowercasing the group name.
 	if f.L1.Kind == protoreflect.GroupKind {
diff --git a/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go b/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go
index 099b2bf4..9aa7a9bb 100644
--- a/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go
+++ b/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go
@@ -424,27 +424,34 @@ func (d *Decoder) parseFieldName() (tok Token, err error) {
 	return Token{}, d.newSyntaxError("invalid field name: %s", errId(d.in))
 }
 
-// parseTypeName parses Any type URL or extension field name. The name is
-// enclosed in [ and ] characters. The C++ parser does not handle many legal URL
-// strings. This implementation is more liberal and allows for the pattern
-// ^[-_a-zA-Z0-9]+([./][-_a-zA-Z0-9]+)*`). Whitespaces and comments are allowed
-// in between [ ], '.', '/' and the sub names.
+// parseTypeName parses an Any type URL or an extension field name. The name is
+// enclosed in [ and ] characters. We allow almost arbitrary type URL prefixes,
+// closely following the text-format spec [1,2]. We implement "ExtensionName |
+// AnyName" as follows (with some exceptions for backwards compatibility):
+//
+// char      = [-_a-zA-Z0-9]
+// url_char  = char | [.~!$&'()*+,;=] | "%", hex, hex
+//
+// Ident         = char, { char }
+// TypeName      = Ident, { ".", Ident } ;
+// UrlPrefix     = url_char, { url_char | "/" } ;
+// ExtensionName = "[", TypeName, "]" ;
+// AnyName       = "[", UrlPrefix, "/", TypeName, "]" ;
+//
+// Additionally, we allow arbitrary whitespace and comments between [ and ].
+//
+// [1] https://protobuf.dev/reference/protobuf/textformat-spec/#characters
+// [2] https://protobuf.dev/reference/protobuf/textformat-spec/#field-names
 func (d *Decoder) parseTypeName() (Token, error) {
-	startPos := len(d.orig) - len(d.in)
 	// Use alias s to advance first in order to use d.in for error handling.
-	// Caller already checks for [ as first character.
+	// Caller already checks for [ as first character (d.in[0] == '[').
 	s := consume(d.in[1:], 0)
 	if len(s) == 0 {
 		return Token{}, ErrUnexpectedEOF
 	}
 
+	// Collect everything between [ and ] in name.
 	var name []byte
-	for len(s) > 0 && isTypeNameChar(s[0]) {
-		name = append(name, s[0])
-		s = s[1:]
-	}
-	s = consume(s, 0)
-
 	var closed bool
 	for len(s) > 0 && !closed {
 		switch {
@@ -452,23 +459,20 @@ func (d *Decoder) parseTypeName() (Token, error) {
 			s = s[1:]
 			closed = true
 
-		case s[0] == '/', s[0] == '.':
-			if len(name) > 0 && (name[len(name)-1] == '/' || name[len(name)-1] == '.') {
-				return Token{}, d.newSyntaxError("invalid type URL/extension field name: %s",
-					d.orig[startPos:len(d.orig)-len(s)+1])
-			}
+		case s[0] == '/' || isTypeNameChar(s[0]) || isUrlExtraChar(s[0]):
 			name = append(name, s[0])
-			s = s[1:]
-			s = consume(s, 0)
-			for len(s) > 0 && isTypeNameChar(s[0]) {
-				name = append(name, s[0])
-				s = s[1:]
+			s = consume(s[1:], 0)
+
+		// URL percent-encoded chars
+		case s[0] == '%':
+			if len(s) < 3 || !isHexChar(s[1]) || !isHexChar(s[2]) {
+				return Token{}, d.parseTypeNameError(s, 3)
 			}
-			s = consume(s, 0)
+			name = append(name, s[0], s[1], s[2])
+			s = consume(s[3:], 0)
 
 		default:
-			return Token{}, d.newSyntaxError(
-				"invalid type URL/extension field name: %s", d.orig[startPos:len(d.orig)-len(s)+1])
+			return Token{}, d.parseTypeNameError(s, 1)
 		}
 	}
 
@@ -476,15 +480,38 @@ func (d *Decoder) parseTypeName() (Token, error) {
 		return Token{}, ErrUnexpectedEOF
 	}
 
-	// First character cannot be '.'. Last character cannot be '.' or '/'.
-	size := len(name)
-	if size == 0 || name[0] == '.' || name[size-1] == '.' || name[size-1] == '/' {
-		return Token{}, d.newSyntaxError("invalid type URL/extension field name: %s",
-			d.orig[startPos:len(d.orig)-len(s)])
+	// Split collected name on last '/' into urlPrefix and typeName (if '/' is
+	// present).
+	typeName := name
+	if i := bytes.LastIndexByte(name, '/'); i != -1 {
+		urlPrefix := name[:i]
+		typeName = name[i+1:]
+
+		// urlPrefix may be empty (for backwards compatibility).
+		// If non-empty, it must not start with '/'.
+		if len(urlPrefix) > 0 && urlPrefix[0] == '/' {
+			return Token{}, d.parseTypeNameError(s, 0)
+		}
 	}
 
+	// typeName must not be empty (note: "" splits to [""]) and all identifier
+	// parts must not be empty.
+	for _, ident := range bytes.Split(typeName, []byte{'.'}) {
+		if len(ident) == 0 {
+			return Token{}, d.parseTypeNameError(s, 0)
+		}
+	}
+
+	// typeName must not contain any percent-encoded or special URL chars.
+	for _, b := range typeName {
+		if b == '%' || (b != '.' && isUrlExtraChar(b)) {
+			return Token{}, d.parseTypeNameError(s, 0)
+		}
+	}
+
+	startPos := len(d.orig) - len(d.in)
+	endPos := len(d.orig) - len(s)
 	d.in = s
-	endPos := len(d.orig) - len(d.in)
 	d.consume(0)
 
 	return Token{
@@ -496,16 +523,32 @@ func (d *Decoder) parseTypeName() (Token, error) {
 	}, nil
 }
 
+func (d *Decoder) parseTypeNameError(s []byte, numUnconsumedChars int) error {
+	return d.newSyntaxError(
+		"invalid type URL/extension field name: %s",
+		d.in[:len(d.in)-len(s)+min(numUnconsumedChars, len(s))],
+	)
+}
+
+func isHexChar(b byte) bool {
+	return ('0' <= b && b <= '9') ||
+		('a' <= b && b <= 'f') ||
+		('A' <= b && b <= 'F')
+}
+
 func isTypeNameChar(b byte) bool {
-	return (b == '-' || b == '_' ||
+	return b == '-' || b == '_' ||
 		('0' <= b && b <= '9') ||
 		('a' <= b && b <= 'z') ||
-		('A' <= b && b <= 'Z'))
+		('A' <= b && b <= 'Z')
 }
 
-func isWhiteSpace(b byte) bool {
+// isUrlExtraChar complements isTypeNameChar with extra characters that we allow
+// in URLs but not in type names. Note that '/' is not included so that it can
+// be treated specially.
+func isUrlExtraChar(b byte) bool {
 	switch b {
-	case ' ', '\n', '\r', '\t':
+	case '.', '~', '!', '$', '&', '(', ')', '*', '+', ',', ';', '=':
 		return true
 	default:
 		return false
diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc.go
index 688aabe4..c775e583 100644
--- a/vendor/google.golang.org/protobuf/internal/filedesc/desc.go
+++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc.go
@@ -32,6 +32,7 @@ const (
 	EditionProto3      Edition = 999
 	Edition2023        Edition = 1000
 	Edition2024        Edition = 1001
+	EditionUnstable    Edition = 9999
 	EditionUnsupported Edition = 100000
 )
 
@@ -72,9 +73,10 @@ type (
 		EditionFeatures EditionFeatures
 	}
 	FileL2 struct {
-		Options   func() protoreflect.ProtoMessage
-		Imports   FileImports
-		Locations SourceLocations
+		Options       func() protoreflect.ProtoMessage
+		Imports       FileImports
+		OptionImports func() protoreflect.FileImports
+		Locations     SourceLocations
 	}
 
 	// EditionFeatures is a frequently-instantiated struct, so please take care
@@ -126,12 +128,9 @@ func (fd *File) ParentFile() protoreflect.FileDescriptor { return fd }
 func (fd *File) Parent() protoreflect.Descriptor         { return nil }
 func (fd *File) Index() int                              { return 0 }
 func (fd *File) Syntax() protoreflect.Syntax             { return fd.L1.Syntax }
-
-// Not exported and just used to reconstruct the original FileDescriptor proto
-func (fd *File) Edition() int32                  { return int32(fd.L1.Edition) }
-func (fd *File) Name() protoreflect.Name         { return fd.L1.Package.Name() }
-func (fd *File) FullName() protoreflect.FullName { return fd.L1.Package }
-func (fd *File) IsPlaceholder() bool             { return false }
+func (fd *File) Name() protoreflect.Name                 { return fd.L1.Package.Name() }
+func (fd *File) FullName() protoreflect.FullName         { return fd.L1.Package }
+func (fd *File) IsPlaceholder() bool                     { return false }
 func (fd *File) Options() protoreflect.ProtoMessage {
 	if f := fd.lazyInit().Options; f != nil {
 		return f()
@@ -150,6 +149,16 @@ func (fd *File) Format(s fmt.State, r rune)                    { descfmt.FormatD
 func (fd *File) ProtoType(protoreflect.FileDescriptor)         {}
 func (fd *File) ProtoInternal(pragma.DoNotImplement)           {}
 
+// The next two are not part of the FileDescriptor interface. They are just used to reconstruct
+// the original FileDescriptor proto.
+func (fd *File) Edition() int32 { return int32(fd.L1.Edition) }
+func (fd *File) OptionImports() protoreflect.FileImports {
+	if f := fd.lazyInit().OptionImports; f != nil {
+		return f()
+	}
+	return emptyFiles
+}
+
 func (fd *File) lazyInit() *FileL2 {
 	if atomic.LoadUint32(&fd.once) == 0 {
 		fd.lazyInitOnce()
@@ -182,9 +191,9 @@ type (
 		L2 *EnumL2 // protected by fileDesc.once
 	}
 	EnumL1 struct {
-		eagerValues bool // controls whether EnumL2.Values is already populated
-
 		EditionFeatures EditionFeatures
+		Visibility      int32
+		eagerValues     bool // controls whether EnumL2.Values is already populated
 	}
 	EnumL2 struct {
 		Options        func() protoreflect.ProtoMessage
@@ -219,6 +228,11 @@ func (ed *Enum) ReservedNames() protoreflect.Names       { return &ed.lazyInit()
 func (ed *Enum) ReservedRanges() protoreflect.EnumRanges { return &ed.lazyInit().ReservedRanges }
 func (ed *Enum) Format(s fmt.State, r rune)              { descfmt.FormatDesc(s, r, ed) }
 func (ed *Enum) ProtoType(protoreflect.EnumDescriptor)   {}
+
+// This is not part of the EnumDescriptor interface. It is just used to reconstruct
+// the original FileDescriptor proto.
+func (ed *Enum) Visibility() int32 { return ed.L1.Visibility }
+
 func (ed *Enum) lazyInit() *EnumL2 {
 	ed.L0.ParentFile.lazyInit() // implicitly initializes L2
 	return ed.L2
@@ -244,13 +258,13 @@ type (
 		L2 *MessageL2 // protected by fileDesc.once
 	}
 	MessageL1 struct {
-		Enums        Enums
-		Messages     Messages
-		Extensions   Extensions
-		IsMapEntry   bool // promoted from google.protobuf.MessageOptions
-		IsMessageSet bool // promoted from google.protobuf.MessageOptions
-
+		Enums           Enums
+		Messages        Messages
+		Extensions      Extensions
 		EditionFeatures EditionFeatures
+		Visibility      int32
+		IsMapEntry      bool // promoted from google.protobuf.MessageOptions
+		IsMessageSet    bool // promoted from google.protobuf.MessageOptions
 	}
 	MessageL2 struct {
 		Options               func() protoreflect.ProtoMessage
@@ -319,6 +333,11 @@ func (md *Message) Messages() protoreflect.MessageDescriptors     { return &md.L
 func (md *Message) Extensions() protoreflect.ExtensionDescriptors { return &md.L1.Extensions }
 func (md *Message) ProtoType(protoreflect.MessageDescriptor)      {}
 func (md *Message) Format(s fmt.State, r rune)                    { descfmt.FormatDesc(s, r, md) }
+
+// This is not part of the MessageDescriptor interface. It is just used to reconstruct
+// the original FileDescriptor proto.
+func (md *Message) Visibility() int32 { return md.L1.Visibility }
+
 func (md *Message) lazyInit() *MessageL2 {
 	md.L0.ParentFile.lazyInit() // implicitly initializes L2
 	return md.L2
diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go
index d2f54949..e91860f5 100644
--- a/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go
+++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go
@@ -284,6 +284,13 @@ func (ed *Enum) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protorefl
 			case genid.EnumDescriptorProto_Value_field_number:
 				numValues++
 			}
+		case protowire.VarintType:
+			v, m := protowire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case genid.EnumDescriptorProto_Visibility_field_number:
+				ed.L1.Visibility = int32(v)
+			}
 		default:
 			m := protowire.ConsumeFieldValue(num, typ, b)
 			b = b[m:]
@@ -365,6 +372,13 @@ func (md *Message) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protor
 				md.unmarshalSeedOptions(v)
 			}
 			prevField = num
+		case protowire.VarintType:
+			v, m := protowire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case genid.DescriptorProto_Visibility_field_number:
+				md.L1.Visibility = int32(v)
+			}
 		default:
 			m := protowire.ConsumeFieldValue(num, typ, b)
 			b = b[m:]
diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go
index d4c94458..78f02b1b 100644
--- a/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go
+++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go
@@ -134,6 +134,7 @@ func (fd *File) unmarshalFull(b []byte) {
 
 	var enumIdx, messageIdx, extensionIdx, serviceIdx int
 	var rawOptions []byte
+	var optionImports []string
 	fd.L2 = new(FileL2)
 	for len(b) > 0 {
 		num, typ, n := protowire.ConsumeTag(b)
@@ -157,6 +158,8 @@ func (fd *File) unmarshalFull(b []byte) {
 					imp = PlaceholderFile(path)
 				}
 				fd.L2.Imports = append(fd.L2.Imports, protoreflect.FileImport{FileDescriptor: imp})
+			case genid.FileDescriptorProto_OptionDependency_field_number:
+				optionImports = append(optionImports, sb.MakeString(v))
 			case genid.FileDescriptorProto_EnumType_field_number:
 				fd.L1.Enums.List[enumIdx].unmarshalFull(v, sb)
 				enumIdx++
@@ -178,6 +181,23 @@ func (fd *File) unmarshalFull(b []byte) {
 		}
 	}
 	fd.L2.Options = fd.builder.optionsUnmarshaler(&descopts.File, rawOptions)
+	if len(optionImports) > 0 {
+		var imps FileImports
+		var once sync.Once
+		fd.L2.OptionImports = func() protoreflect.FileImports {
+			once.Do(func() {
+				imps = make(FileImports, len(optionImports))
+				for i, path := range optionImports {
+					imp, _ := fd.builder.FileRegistry.FindFileByPath(path)
+					if imp == nil {
+						imp = PlaceholderFile(path)
+					}
+					imps[i] = protoreflect.FileImport{FileDescriptor: imp}
+				}
+			})
+			return &imps
+		}
+	}
 }
 
 func (ed *Enum) unmarshalFull(b []byte, sb *strs.Builder) {
@@ -310,7 +330,6 @@ func (md *Message) unmarshalFull(b []byte, sb *strs.Builder) {
 				md.L1.Extensions.List[extensionIdx].unmarshalFull(v, sb)
 				extensionIdx++
 			case genid.DescriptorProto_Options_field_number:
-				md.unmarshalOptions(v)
 				rawOptions = appendOptions(rawOptions, v)
 			}
 		default:
@@ -336,27 +355,6 @@ func (md *Message) unmarshalFull(b []byte, sb *strs.Builder) {
 	md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Message, rawOptions)
 }
 
-func (md *Message) unmarshalOptions(b []byte) {
-	for len(b) > 0 {
-		num, typ, n := protowire.ConsumeTag(b)
-		b = b[n:]
-		switch typ {
-		case protowire.VarintType:
-			v, m := protowire.ConsumeVarint(b)
-			b = b[m:]
-			switch num {
-			case genid.MessageOptions_MapEntry_field_number:
-				md.L1.IsMapEntry = protowire.DecodeBool(v)
-			case genid.MessageOptions_MessageSetWireFormat_field_number:
-				md.L1.IsMessageSet = protowire.DecodeBool(v)
-			}
-		default:
-			m := protowire.ConsumeFieldValue(num, typ, b)
-			b = b[m:]
-		}
-	}
-}
-
 func unmarshalMessageReservedRange(b []byte) (r [2]protoreflect.FieldNumber) {
 	for len(b) > 0 {
 		num, typ, n := protowire.ConsumeTag(b)
diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/editions.go b/vendor/google.golang.org/protobuf/internal/filedesc/editions.go
index a0aad277..66ba9068 100644
--- a/vendor/google.golang.org/protobuf/internal/filedesc/editions.go
+++ b/vendor/google.golang.org/protobuf/internal/filedesc/editions.go
@@ -13,8 +13,10 @@ import (
 	"google.golang.org/protobuf/reflect/protoreflect"
 )
 
-var defaultsCache = make(map[Edition]EditionFeatures)
-var defaultsKeys = []Edition{}
+var (
+	defaultsCache = make(map[Edition]EditionFeatures)
+	defaultsKeys  = []Edition{}
+)
 
 func init() {
 	unmarshalEditionDefaults(editiondefaults.Defaults)
@@ -41,7 +43,7 @@ func unmarshalGoFeature(b []byte, parent EditionFeatures) EditionFeatures {
 			b = b[m:]
 			parent.StripEnumPrefix = int(v)
 		default:
-			panic(fmt.Sprintf("unkown field number %d while unmarshalling GoFeatures", num))
+			panic(fmt.Sprintf("unknown field number %d while unmarshalling GoFeatures", num))
 		}
 	}
 	return parent
@@ -76,7 +78,7 @@ func unmarshalFeatureSet(b []byte, parent EditionFeatures) EditionFeatures {
 				// DefaultSymbolVisibility is enforced in protoc, runtimes should not
 				// inspect this value.
 			default:
-				panic(fmt.Sprintf("unkown field number %d while unmarshalling FeatureSet", num))
+				panic(fmt.Sprintf("unknown field number %d while unmarshalling FeatureSet", num))
 			}
 		case protowire.BytesType:
 			v, m := protowire.ConsumeBytes(b)
@@ -150,7 +152,7 @@ func unmarshalEditionDefaults(b []byte) {
 			_, m := protowire.ConsumeVarint(b)
 			b = b[m:]
 		default:
-			panic(fmt.Sprintf("unkown field number %d while unmarshalling EditionDefault", num))
+			panic(fmt.Sprintf("unknown field number %d while unmarshalling EditionDefault", num))
 		}
 	}
 }
diff --git a/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go b/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go
index 950a6a32..65aaf4d2 100644
--- a/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go
+++ b/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go
@@ -26,6 +26,7 @@ const (
 	Edition_EDITION_PROTO3_enum_value          = 999
 	Edition_EDITION_2023_enum_value            = 1000
 	Edition_EDITION_2024_enum_value            = 1001
+	Edition_EDITION_UNSTABLE_enum_value        = 9999
 	Edition_EDITION_1_TEST_ONLY_enum_value     = 1
 	Edition_EDITION_2_TEST_ONLY_enum_value     = 2
 	Edition_EDITION_99997_TEST_ONLY_enum_value = 99997
diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_map.go b/vendor/google.golang.org/protobuf/internal/impl/codec_map.go
index 229c6980..4a3bf393 100644
--- a/vendor/google.golang.org/protobuf/internal/impl/codec_map.go
+++ b/vendor/google.golang.org/protobuf/internal/impl/codec_map.go
@@ -113,6 +113,9 @@ func sizeMap(mapv reflect.Value, mapi *mapInfo, f *coderFieldInfo, opts marshalO
 }
 
 func consumeMap(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
+	if opts.depth--; opts.depth < 0 {
+		return out, errRecursionDepth
+	}
 	if wtyp != protowire.BytesType {
 		return out, errUnknown
 	}
@@ -170,6 +173,9 @@ func consumeMap(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo
 }
 
 func consumeMapOfMessage(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
+	if opts.depth--; opts.depth < 0 {
+		return out, errRecursionDepth
+	}
 	if wtyp != protowire.BytesType {
 		return out, errUnknown
 	}
diff --git a/vendor/google.golang.org/protobuf/internal/impl/decode.go b/vendor/google.golang.org/protobuf/internal/impl/decode.go
index e0dd21fa..1228b5c8 100644
--- a/vendor/google.golang.org/protobuf/internal/impl/decode.go
+++ b/vendor/google.golang.org/protobuf/internal/impl/decode.go
@@ -102,8 +102,7 @@ var errUnknown = errors.New("unknown")
 
 func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) {
 	mi.init()
-	opts.depth--
-	if opts.depth < 0 {
+	if opts.depth--; opts.depth < 0 {
 		return out, errRecursionDepth
 	}
 	if flags.ProtoLegacy && mi.isMessageSet {
diff --git a/vendor/google.golang.org/protobuf/internal/impl/validate.go b/vendor/google.golang.org/protobuf/internal/impl/validate.go
index 7b2995dd..99a1eb95 100644
--- a/vendor/google.golang.org/protobuf/internal/impl/validate.go
+++ b/vendor/google.golang.org/protobuf/internal/impl/validate.go
@@ -68,9 +68,13 @@ func Validate(mt protoreflect.MessageType, in protoiface.UnmarshalInput) (out pr
 	if in.Resolver == nil {
 		in.Resolver = protoregistry.GlobalTypes
 	}
+	if in.Depth == 0 {
+		in.Depth = protowire.DefaultRecursionLimit
+	}
 	o, st := mi.validate(in.Buf, 0, unmarshalOptions{
 		flags:    in.Flags,
 		resolver: in.Resolver,
+		depth:    in.Depth,
 	})
 	if o.initialized {
 		out.Flags |= protoiface.UnmarshalInitialized
@@ -257,6 +261,9 @@ func (mi *MessageInfo) validate(b []byte, groupTag protowire.Number, opts unmars
 		states[0].typ = validationTypeGroup
 		states[0].endGroup = groupTag
 	}
+	if opts.depth--; opts.depth < 0 {
+		return out, ValidationInvalid
+	}
 	initialized := true
 	start := len(b)
 State:
@@ -451,6 +458,13 @@ State:
 						mi:      vi.mi,
 						tail:    b,
 					})
+					if vi.typ == validationTypeMessage ||
+						vi.typ == validationTypeGroup ||
+						vi.typ == validationTypeMap {
+						if opts.depth--; opts.depth < 0 {
+							return out, ValidationInvalid
+						}
+					}
 					b = v
 					continue State
 				case validationTypeRepeatedVarint:
@@ -499,6 +513,9 @@ State:
 						mi:       vi.mi,
 						endGroup: num,
 					})
+					if opts.depth--; opts.depth < 0 {
+						return out, ValidationInvalid
+					}
 					continue State
 				case flags.ProtoLegacy && vi.typ == validationTypeMessageSetItem:
 					typeid, v, n, err := messageset.ConsumeFieldValue(b, false)
@@ -521,6 +538,13 @@ State:
 							mi:   xvi.mi,
 							tail: b[n:],
 						})
+						if xvi.typ == validationTypeMessage ||
+							xvi.typ == validationTypeGroup ||
+							xvi.typ == validationTypeMap {
+							if opts.depth--; opts.depth < 0 {
+								return out, ValidationInvalid
+							}
+						}
 						b = v
 						continue State
 					}
@@ -547,12 +571,14 @@ State:
 		switch st.typ {
 		case validationTypeMessage, validationTypeGroup:
 			numRequiredFields = int(st.mi.numRequiredFields)
+			opts.depth++
 		case validationTypeMap:
 			// If this is a map field with a message value that contains
 			// required fields, require that the value be present.
 			if st.mi != nil && st.mi.numRequiredFields > 0 {
 				numRequiredFields = 1
 			}
+			opts.depth++
 		}
 		// If there are more than 64 required fields, this check will
 		// always fail and we will report that the message is potentially
diff --git a/vendor/google.golang.org/protobuf/internal/version/version.go b/vendor/google.golang.org/protobuf/internal/version/version.go
index 697d1c14..763fd828 100644
--- a/vendor/google.golang.org/protobuf/internal/version/version.go
+++ b/vendor/google.golang.org/protobuf/internal/version/version.go
@@ -52,7 +52,7 @@ import (
 const (
 	Major      = 1
 	Minor      = 36
-	Patch      = 8
+	Patch      = 11
 	PreRelease = ""
 )
 
diff --git a/vendor/google.golang.org/protobuf/proto/decode.go b/vendor/google.golang.org/protobuf/proto/decode.go
index 4cbf1aea..889d8511 100644
--- a/vendor/google.golang.org/protobuf/proto/decode.go
+++ b/vendor/google.golang.org/protobuf/proto/decode.go
@@ -121,9 +121,8 @@ func (o UnmarshalOptions) unmarshal(b []byte, m protoreflect.Message) (out proto
 
 		out, err = methods.Unmarshal(in)
 	} else {
-		o.RecursionLimit--
-		if o.RecursionLimit < 0 {
-			return out, errors.New("exceeded max recursion depth")
+		if o.RecursionLimit--; o.RecursionLimit < 0 {
+			return out, errRecursionDepth
 		}
 		err = o.unmarshalMessageSlow(b, m)
 	}
@@ -220,6 +219,9 @@ func (o UnmarshalOptions) unmarshalSingular(b []byte, wtyp protowire.Type, m pro
 }
 
 func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp protowire.Type, mapv protoreflect.Map, fd protoreflect.FieldDescriptor) (n int, err error) {
+	if o.RecursionLimit--; o.RecursionLimit < 0 {
+		return 0, errRecursionDepth
+	}
 	if wtyp != protowire.BytesType {
 		return 0, errUnknown
 	}
@@ -305,3 +307,5 @@ func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp protowire.Type, mapv proto
 var errUnknown = errors.New("BUG: internal error (unknown)")
 
 var errDecode = errors.New("cannot parse invalid wire-format data")
+
+var errRecursionDepth = errors.New("exceeded maximum recursion depth")
diff --git a/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go b/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go
index 06d584c1..484c21fd 100644
--- a/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go
+++ b/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go
@@ -172,13 +172,14 @@ import (
 // ) to obtain a formatter capable of generating timestamps in this format.
 type Timestamp struct {
 	state protoimpl.MessageState `protogen:"open.v1"`
-	// Represents seconds of UTC time since Unix epoch
-	// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
-	// 9999-12-31T23:59:59Z inclusive.
+	// Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must
+	// be between -315576000000 and 315576000000 inclusive (which corresponds to
+	// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z).
 	Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"`
-	// Non-negative fractions of a second at nanosecond resolution. Negative
-	// second values with fractions must still have non-negative nanos values
-	// that count forward in time. Must be from 0 to 999,999,999
+	// Non-negative fractions of a second at nanosecond resolution. This field is
+	// the nanosecond portion of the duration, not an alternative to seconds.
+	// Negative second values with fractions must still have non-negative nanos
+	// values that count forward in time. Must be between 0 and 999,999,999
 	// inclusive.
 	Nanos         int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"`
 	unknownFields protoimpl.UnknownFields
diff --git a/vendor/honnef.co/go/tools/analysis/code/code.go b/vendor/honnef.co/go/tools/analysis/code/code.go
index 9e479192..e87a1d1d 100644
--- a/vendor/honnef.co/go/tools/analysis/code/code.go
+++ b/vendor/honnef.co/go/tools/analysis/code/code.go
@@ -16,7 +16,6 @@ import (
 	"honnef.co/go/tools/analysis/facts/generated"
 	"honnef.co/go/tools/analysis/facts/purity"
 	"honnef.co/go/tools/analysis/facts/tokenfile"
-	"honnef.co/go/tools/go/ast/astutil"
 	"honnef.co/go/tools/go/types/typeutil"
 	"honnef.co/go/tools/knowledge"
 	"honnef.co/go/tools/pattern"
@@ -92,15 +91,18 @@ func SelectorName(pass *analysis.Pass, expr *ast.SelectorExpr) string {
 	info := pass.TypesInfo
 	sel := info.Selections[expr]
 	if sel == nil {
-		if x, ok := expr.X.(*ast.Ident); ok {
+		switch x := expr.X.(type) {
+		case *ast.Ident:
 			pkg, ok := info.ObjectOf(x).(*types.PkgName)
 			if !ok {
-				// This shouldn't happen
-				return fmt.Sprintf("%s.%s", x.Name, expr.Sel.Name)
+				return fmt.Sprintf("(%s).%s", info.TypeOf(x), expr.Sel.Name)
 			}
 			return fmt.Sprintf("%s.%s", pkg.Imported().Path(), expr.Sel.Name)
+		case *ast.SelectorExpr:
+			return fmt.Sprintf("(%s).%s", SelectorName(pass, x), expr.Sel.Name)
+		default:
+			panic(fmt.Sprintf("unsupported selector: %v", expr))
 		}
-		panic(fmt.Sprintf("unsupported selector: %v", expr))
 	}
 	if v, ok := sel.Obj().(*types.Var); ok && v.IsField() {
 		return fmt.Sprintf("(%s).%s", typeutil.DereferenceR(sel.Recv()), sel.Obj().Name())
@@ -169,7 +171,7 @@ func CallName(pass *analysis.Pass, call *ast.CallExpr) string {
 	// See the comment in typeutil.FuncName for why this doesn't require special handling
 	// of aliases.
 
-	fun := astutil.Unparen(call.Fun)
+	fun := ast.Unparen(call.Fun)
 
 	// Instantiating a function cannot return another generic function, so doing this once is enough
 	switch idx := fun.(type) {
diff --git a/vendor/honnef.co/go/tools/analysis/code/visit.go b/vendor/honnef.co/go/tools/analysis/code/visit.go
index 83b585d9..18271e91 100644
--- a/vendor/honnef.co/go/tools/analysis/code/visit.go
+++ b/vendor/honnef.co/go/tools/analysis/code/visit.go
@@ -9,8 +9,8 @@ import (
 	"iter"
 	"slices"
 
-	typeindexanalyzer "honnef.co/go/tools/internal/analysisinternal/typeindex"
-	"honnef.co/go/tools/internal/typesinternal/typeindex"
+	typeindexanalyzer "honnef.co/go/tools/internal/xtools-internal/analysis/typeindex"
+	"honnef.co/go/tools/internal/xtools-internal/typesinternal/typeindex"
 	"honnef.co/go/tools/pattern"
 
 	"golang.org/x/tools/go/analysis"
diff --git a/vendor/honnef.co/go/tools/analysis/dfa/dense/flow.go b/vendor/honnef.co/go/tools/analysis/dfa/dense/flow.go
new file mode 100644
index 00000000..efa573c5
--- /dev/null
+++ b/vendor/honnef.co/go/tools/analysis/dfa/dense/flow.go
@@ -0,0 +1,60 @@
+// Copyright 2026 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package flow implements a monotone flow analysis framework.
+package dense
+
+import (
+	"cmp"
+	"slices"
+
+	"honnef.co/go/tools/internal/xtools-internal/graph"
+)
+
+const debug = false
+
+// Analysis is the result of a monotone analysis. Fact is the type of elements
+// in the analysis semilattice, and represents the outcome of the analysis at
+// every node and edge.
+type Analysis[Fact any, NodeID comparable] struct {
+	nodeMap *graph.Index[NodeID]
+	ins     []Fact           // By NodeID
+	edges   []edgeFact[Fact] // Sorted by (from, to)
+}
+
+// In returns the analysis fact on entry to nid. This is the merge of the facts
+// on all incoming edges.
+func (a *Analysis[Fact, NodeID]) In(nid NodeID) Fact {
+	return a.ins[a.nodeMap.Index(nid)]
+}
+
+// Edge returns the analysis fact propagated on edge from ==> to.
+func (a *Analysis[Fact, NodeID]) Edge(from, to NodeID) Fact {
+	i, found := slices.BinarySearchFunc(a.edges, a.edge(from, to), edgeFact[Fact].compare)
+	if !found {
+		panic("no such edge")
+	}
+	return a.edges[i].fact
+}
+
+func (a *Analysis[Fact, NodeID]) edge(from, to NodeID) edge {
+	fromNum, toNum := a.nodeMap.Index(from), a.nodeMap.Index(to)
+	return edge{fromNum, toNum}
+}
+
+type edge struct {
+	from, to int
+}
+
+func (e edge) compare(f edge) int {
+	if v := cmp.Compare(e.from, f.from); v != 0 {
+		return v
+	}
+	return cmp.Compare(e.to, f.to)
+}
+
+type edgeFact[Fact any] struct {
+	edge
+	fact Fact
+}
diff --git a/vendor/honnef.co/go/tools/analysis/dfa/dense/forward.go b/vendor/honnef.co/go/tools/analysis/dfa/dense/forward.go
new file mode 100644
index 00000000..7837f640
--- /dev/null
+++ b/vendor/honnef.co/go/tools/analysis/dfa/dense/forward.go
@@ -0,0 +1,271 @@
+// Copyright 2026 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package dense
+
+import (
+	"container/heap"
+	"log"
+	"slices"
+
+	"honnef.co/go/tools/analysis/dfa"
+	"honnef.co/go/tools/internal/xtools-internal/graph"
+)
+
+// Forward performs a forward monotone analysis over a control flow graph.
+//
+// The entry map provides initial state for entry blocks (blocks with zero
+// predecessors). For each edge, it calls transfer(fact, edge), where fact is
+// the analysis state on entry to edge.Pred. The transfer function must return
+// the outgoing analysis state of the edge (which may be fact, if the edge has
+// no effect on the  analysis state).
+func Forward[L dfa.Semilattice[Fact], Fact any, NodeID comparable](g graph.Graph[NodeID], entry map[NodeID]Fact, transfer func(from, to NodeID, fact Fact) Fact) *Analysis[Fact, NodeID] {
+	cg, nodeMap := graph.Compact(g)
+
+	nNodes := cg.NumNodes()
+	fb := &fwdBuilder[L, Fact, NodeID]{
+		cfg:      cg,
+		nodeMap:  nodeMap,
+		transfer: transfer,
+		blocks:   make([]blockInfo[Fact], nNodes),
+	}
+	fb.queue.init(cg)
+
+	// Initialize each node.
+	totalEdges := 0
+	for ni := range nNodes {
+		b := &fb.blocks[ni]
+
+		// Construct back-edges.
+		//
+		// I experimented with making Graph support iterating over in-edges, but
+		// in practice that just meant each Graph implementation had a copy of
+		// this logic. So instead we keep Graph as simple as possible and
+		// compute the auxiliary data in the algorithm. One drawback of this is
+		// that, for the [Transpose] graph, this information is redundant with
+		// the underlying graph. We could potentially special-case that.
+		outs := 0
+		for succID := range cg.Out(ni) {
+			succ := &fb.blocks[succID]
+			succ.preds = append(succ.preds, blockEdge{ni, outs})
+			outs++
+			totalEdges++
+		}
+
+		// Initialize in & out states.
+		fact, ok := entry[nodeMap.Value(ni)]
+		if !ok {
+			fact = fb.l.Ident()
+		}
+		b.in = fact
+		b.out = slices.Repeat([]Fact{fb.l.Ident()}, outs)
+
+		// Enqueue block.
+		//
+		// It's tempting to enqueue only the entry blocks, but this is wrong.
+		// The entry map may be empty if there are no interesting entry states,
+		// but the transfer function may still introduce interesting states
+		// anywhere.
+		b.dirty = true
+		fb.queue.enqueue(ni)
+	}
+
+	// Propagate over blocks.
+	fb.propagate()
+
+	// Collect the final analysis results.
+	a := Analysis[Fact, NodeID]{
+		nodeMap: nodeMap,
+		ins:     make([]Fact, nNodes),
+		edges:   make([]edgeFact[Fact], 0, totalEdges),
+	}
+	for pred := range nNodes {
+		a.ins[pred] = fb.blocks[pred].in
+		i := 0
+		for succ := range cg.Out(pred) {
+			edge := edge{pred, succ}
+			a.edges = append(a.edges, edgeFact[Fact]{edge, fb.blocks[pred].out[i]})
+			i++
+		}
+	}
+	slices.SortFunc(a.edges, func(a, b edgeFact[Fact]) int { return a.edge.compare(b.edge) })
+	return &a
+}
+
+// fwdBuilder is the state used during [Forward] analysis.
+type fwdBuilder[L dfa.Semilattice[Fact], Fact any, NodeID comparable] struct {
+	l L // Lattice
+
+	cfg     graph.Graph[int]     // Control flow graph (compact)
+	nodeMap *graph.Index[NodeID] // Map from cfg to original NodeIDs
+
+	// transfer is the edge transfer function.
+	transfer func(from, to NodeID, fact Fact) Fact
+
+	blocks []blockInfo[Fact]
+
+	queue nodeHeap
+}
+
+type blockInfo[Fact any] struct {
+	dirty bool // The in fact has never been propagated.
+
+	preds []blockEdge
+
+	in  Fact
+	out []Fact // Corresponds to i'th out edge
+}
+
+type blockEdge struct {
+	node int
+	i    int // Out edge index
+}
+
+// nodeHeap implements a heap of NodeIDs, ordered topologically.
+//
+// We use this ordering so forward analysis converges more quickly.
+type nodeHeap struct {
+	heap        []int   // Remaining nodes in the current sweep
+	deferred    []int   // Nodes of next sweep
+	inQueue     []int64 // Bitmap over node IDs
+	prio        []int   // NodeID -> priority
+	currentPrio int     // Priority of last dequeued node, or -1
+}
+
+func (h *nodeHeap) init(g graph.Graph[int]) {
+	nNodes := g.NumNodes()
+	*h = nodeHeap{
+		inQueue:     make([]int64, (nNodes+63)/64),
+		prio:        make([]int, nNodes),
+		currentPrio: -1,
+	}
+	for p, nid := range graph.ReversePostorder(g) {
+		h.prio[nid] = p
+	}
+}
+
+func (h *nodeHeap) enqueue(nid int) {
+	if h.inQueue[nid/64]&(1<<(nid%64)) != 0 {
+		return
+	}
+	h.inQueue[nid/64] |= 1 << (nid % 64)
+
+	if h.currentPrio >= 0 && h.prio[nid] <= h.currentPrio {
+		// This is a retreating edge, self-edge, or other update to a node
+		// already passed in this sweep. Coalesce it into the next sweep.
+		h.deferred = append(h.deferred, nid)
+	} else {
+		heap.Push(h, nid)
+	}
+}
+
+func (h *nodeHeap) dequeue() int {
+	if len(h.heap) == 0 {
+		// Start the next RPO sweep.
+		h.heap, h.deferred = h.deferred, h.heap[:0]
+		h.currentPrio = -1
+		heap.Init(h)
+	}
+
+	nid := h.heap[0]
+	heap.Pop(h)
+	h.inQueue[nid/64] &^= 1 << (nid % 64)
+	h.currentPrio = h.prio[nid]
+	return nid
+}
+
+func (h *nodeHeap) pending() bool     { return len(h.heap) != 0 || len(h.deferred) != 0 }
+func (h nodeHeap) Len() int           { return len(h.heap) }
+func (h nodeHeap) Less(i, j int) bool { return h.prio[h.heap[i]] < h.prio[h.heap[j]] }
+func (h nodeHeap) Swap(i, j int)      { h.heap[i], h.heap[j] = h.heap[j], h.heap[i] }
+func (h *nodeHeap) Push(x any)        { h.heap = append(h.heap, x.(int)) }
+func (h *nodeHeap) Pop() any {
+	n := len(h.heap)
+	x := h.heap[n-1]
+	h.heap = h.heap[:n-1]
+	return x
+}
+
+func (fb *fwdBuilder[L, Fact, NodeID]) merge(a, b Fact) Fact {
+	if fb.l.Equals(a, b) {
+		return a
+	}
+	return fb.l.Merge(a, b)
+}
+
+func (fb *fwdBuilder[L, Fact, NodeID]) propagate() {
+	for fb.queue.pending() {
+		bi := fb.queue.dequeue()
+		block := &fb.blocks[bi]
+
+		// Merge predecessor facts to compute updated "in" fact.
+		var in Fact
+		first := true
+		for _, edge := range block.preds {
+			pred := &fb.blocks[edge.node]
+			var edgeFact Fact
+			if pred.dirty {
+				// We haven't visited this predecessor yet, so it doesn't have
+				// meaningful out facts.
+				edgeFact = fb.l.Ident()
+			} else {
+				edgeFact = pred.out[edge.i]
+			}
+			if first {
+				if debug {
+					log.Printf("propagate to node %d", bi)
+				}
+				in = edgeFact
+				first = false
+			} else {
+				in = fb.merge(in, edgeFact)
+			}
+			if debug {
+				log.Printf("  from node %d: %v", edge.node, edgeFact)
+			}
+		}
+		if first {
+			// No predecessors.
+			if debug {
+				log.Printf("node %d gets initial state", bi)
+			}
+			in = block.in
+		}
+
+		if !block.dirty && fb.l.Equals(in, block.in) {
+			// No change to block input, which means the transfer function
+			// results also won't change from the last time we ran it.
+			if debug {
+				log.Printf("  initial state unchanged: %v", in)
+			}
+			continue
+		}
+		if debug {
+			log.Printf("  new initial state: %v", in)
+		}
+		block.in = in
+
+		// Apply transfer function.
+		predID := fb.nodeMap.Value(bi)
+		i := 0
+		for succNum := range fb.cfg.Out(bi) {
+			edgeFact := fb.transfer(predID, fb.nodeMap.Value(succNum), in)
+			if block.dirty || !fb.l.Equals(block.out[i], edgeFact) {
+				// Out fact changed, so recompute the target block.
+				if debug {
+					log.Printf("  to node %d: %v", succNum, edgeFact)
+				}
+				block.out[i] = edgeFact
+				fb.queue.enqueue(succNum)
+			} else {
+				if debug {
+					log.Printf("  to node %d: no change", succNum)
+				}
+			}
+			i++
+		}
+
+		block.dirty = false
+	}
+}
diff --git a/vendor/honnef.co/go/tools/analysis/dfa/dot.go b/vendor/honnef.co/go/tools/analysis/dfa/dot.go
new file mode 100644
index 00000000..1895aa84
--- /dev/null
+++ b/vendor/honnef.co/go/tools/analysis/dfa/dot.go
@@ -0,0 +1,48 @@
+package dfa
+
+import (
+	"fmt"
+	"strings"
+)
+
+// Dot returns a directed graph in [Graphviz] format that represents the finite
+// join-semilattice ⟨S, ≤⟩. Vertices represent elements in S and edges
+// represent the ≤ relation between elements. We map from ⟨S, ∨⟩ to ⟨S, ≤⟩ by
+// computing x ∨ y for all elements in [S]², where x ≤ y iff x ∨ y == y.
+//
+// The resulting graph can be filtered through [tred] to compute the transitive
+// reduction of the graph, the visualisation of which corresponds to the Hasse
+// diagram of the semilattice.
+//
+// [Graphviz]: https://graphviz.org/
+// [tred]: https://graphviz.org/docs/cli/tred/
+func Dot[L Semilattice[Elem], Elem any](states []Elem) string {
+	var sb strings.Builder
+	sb.WriteString("digraph{\n")
+	sb.WriteString("rankdir=\"BT\"\n")
+
+	for i, v := range states {
+		if vs, ok := any(v).(fmt.Stringer); ok {
+			fmt.Fprintf(&sb, "n%d [label=%q]\n", i, vs)
+		} else {
+			fmt.Fprintf(&sb, "n%d [label=%q]\n", i, fmt.Sprintf("%v", v))
+		}
+	}
+
+	var l L
+
+	for dx, x := range states {
+		for dy, y := range states {
+			if dx == dy {
+				continue
+			}
+
+			if l.Equals(l.Merge(x, y), y) {
+				fmt.Fprintf(&sb, "n%d -> n%d\n", dx, dy)
+			}
+		}
+	}
+
+	sb.WriteString("}")
+	return sb.String()
+}
diff --git a/vendor/honnef.co/go/tools/analysis/dfa/lattice.go b/vendor/honnef.co/go/tools/analysis/dfa/lattice.go
new file mode 100644
index 00000000..672875b6
--- /dev/null
+++ b/vendor/honnef.co/go/tools/analysis/dfa/lattice.go
@@ -0,0 +1,147 @@
+// Copyright 2026 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package dfa
+
+import (
+	"fmt"
+	"maps"
+	"slices"
+)
+
+// A Semilattice describes a bounded semilattice over Elem.
+// That is, a partial order over values of type Elem, with a binary
+// Merge operator and an identity element.
+//
+// This is typically implemented by a stateless type, and acts as a factory for
+// lattice elements.
+type Semilattice[Elem any] interface {
+	// Ident returns the identity element of this lattice, that is the unit of
+	// the Merge operation.
+	Ident() Elem
+
+	// Equals returns whether a and b are the same element.
+	Equals(a, b Elem) bool
+
+	// Merge combines two lattice values, such as the two possible values of a
+	// variable at the end of an if/else statement.
+	//
+	// Merge must satisfy the following identities, where we use ∧ for Merge, =
+	// for Equals, and 𝟏 for Ident:
+	//
+	// - Associativity: x ∧ (y ∧ z) = (x ∧ y) ∧ z
+	// - Commutativity: x ∧ y = y ∧ x
+	// - Idempotency: x ∧ x = x
+	// - Identity: x ∧ 𝟏 = x
+	Merge(a, b Elem) Elem
+}
+
+// A MapLattice implements [Semilattice][map[Key]Elem]. The values in the map
+// are themselves defined by [Semilattice] L.
+//
+// Any elements missing from the map are implicitly L's identity element, and
+// L's identity element never appears as a value in the map.
+//
+// For densely numbered keys, consider using [DenseMapLattice] instead.
+type MapLattice[Key comparable, Elem any, L Semilattice[Elem]] struct {
+	l L
+}
+
+func (m MapLattice[Key, Elem, L]) Ident() map[Key]Elem {
+	return nil
+}
+
+func (m MapLattice[Key, Elem, L]) Equals(a, b map[Key]Elem) bool {
+	return maps.EqualFunc(a, b, m.l.Equals)
+}
+
+func (m MapLattice[Key, Elem, L]) Merge(a, b map[Key]Elem) map[Key]Elem {
+	if len(a) == 0 {
+		return b
+	} else if len(b) == 0 {
+		return a
+	}
+
+	// We need to consider the union of keys in a and b.
+	out := make(map[Key]Elem)
+	id := m.l.Ident()
+	for k, av := range a {
+		bv, ok := b[k]
+		if !ok {
+			// Because Merge(x, Ident()) == x, we can skip calling L.Merge.
+			out[k] = av
+			continue
+		}
+
+		w := m.l.Merge(av, bv)
+		if m.l.Equals(w, id) {
+			// In a semilattice, Merge(x, y) = Ident is only possible when x ==
+			// Ident and y == Ident.
+			panic(fmt.Sprintf(
+				"%T is not a semilattice: Merge(%v, %v) returned Ident for non-Ident arguments",
+				m.l, av, bv))
+		}
+		out[k] = w
+	}
+	// We considered keys that are only in a, and in both a and b. Now we just
+	// need to handle keys that are only in b.
+	for k, v2 := range b {
+		if _, ok := a[k]; !ok {
+			out[k] = v2
+		}
+	}
+
+	return out
+}
+
+// A DenseMapLattice implements [Semilattice][[]Elem]. It is like a [MapLattice]
+// that is indexed by integers. The values in the map are themselves defined by
+// [Semilattice] L.
+//
+// Unlike [MapLattice], L's identity element may appear as a value in the map,
+// to allow for gaps in the numbering of keys when the identity element is
+// Elem's zero value.
+type DenseMapLattice[Elem any, L Semilattice[Elem]] struct {
+	l L
+}
+
+func (s DenseMapLattice[Elem, L]) Ident() []Elem {
+	return nil
+}
+
+func (s DenseMapLattice[Elem, L]) Equals(a, b []Elem) bool {
+	nmin := min(len(a), len(b))
+	ident := s.l.Ident()
+
+	// Check that up to nmin, all elements in a and b match. If one of a or b
+	// is longer, then its tail nmin:nmax must only contain identity elements.
+	return slices.EqualFunc(a[:nmin], b[:nmin], s.l.Equals) &&
+		!slices.ContainsFunc(a[nmin:], func(e Elem) bool {
+			return !s.l.Equals(e, ident)
+		}) &&
+		!slices.ContainsFunc(b[nmin:], func(e Elem) bool {
+			return !s.l.Equals(e, ident)
+		})
+}
+
+func (s DenseMapLattice[Elem, L]) Merge(a, b []Elem) []Elem {
+	if len(a) == 0 {
+		return b
+	} else if len(b) == 0 {
+		return a
+	}
+	out := make([]Elem, max(len(a), len(b)))
+	for k := range max(len(a), len(b)) {
+		av := s.l.Ident()
+		bv := s.l.Ident()
+		if k < len(a) {
+			av = a[k]
+		}
+		if k < len(b) {
+			bv = b[k]
+		}
+		out[k] = s.l.Merge(av, bv)
+	}
+	return out
+}
diff --git a/vendor/honnef.co/go/tools/analysis/facts/nilness/nilness.go b/vendor/honnef.co/go/tools/analysis/facts/nilness/nilness.go
index a7c91a7a..25b315bc 100644
--- a/vendor/honnef.co/go/tools/analysis/facts/nilness/nilness.go
+++ b/vendor/honnef.co/go/tools/analysis/facts/nilness/nilness.go
@@ -2,203 +2,479 @@ package nilness
 
 import (
 	"fmt"
+	"go/constant"
 	"go/token"
 	"go/types"
 	"reflect"
+	"slices"
+	"strings"
 
+	"honnef.co/go/tools/analysis/dfa"
+	"honnef.co/go/tools/analysis/dfa/dense"
 	"honnef.co/go/tools/go/ir"
 	"honnef.co/go/tools/go/types/typeutil"
 	"honnef.co/go/tools/internal/passes/buildir"
 
+	"golang.org/x/exp/typeparams"
 	"golang.org/x/tools/go/analysis"
 )
 
-// neverReturnsNilFact denotes that a function's return value will never
-// be nil (typed or untyped). The analysis errs on the side of false
-// negatives.
-type neverReturnsNilFact struct {
-	Rets []neverNilness
+// TODO(dh): The analysis is currently entirely forward, which means that for
+//
+// 	x := s[:0]
+// 	y := s[:1]
+// 	z := s[:0]
+//
+// x will have MaybeNil at every program point and s will have MaybeNil before
+// execution of y, even though executing y without panicing tells us that s has
+// been non-nil for all 3 instructions.
+
+type nilnessFact struct {
+	Rets []ValueNilness
 }
 
-func (*neverReturnsNilFact) AFact() {}
-func (fact *neverReturnsNilFact) String() string {
-	return fmt.Sprintf("never returns nil: %v", fact.Rets)
+func (*nilnessFact) AFact() {}
+func (fact *nilnessFact) String() string {
+	return fmt.Sprintf("nilness: %v", fact.Rets)
+}
+
+type ValueNilness struct {
+	// Undefined for non-interface values.
+	// For interface values, whether the stored value may be nil.
+	// Even when Outer == MaybeNil, Inner may still offer precise information
+	// for the cases when Outer is dynamically not nil. For example, {NeverNil,
+	// MaybeNil} states that the interface value might be nil, but if it isn't,
+	// it will definitely contain a non-nil value.
+	Inner Nilness
+	// For non-interface values, whether the value may be nil.
+	// For interface values, whether the interface value may be nil.
+	Outer Nilness
 }
 
 type Result struct {
-	m map[*types.Func][]neverNilness
+	m map[*types.Func][]ValueNilness
 }
 
 var Analysis = &analysis.Analyzer{
 	Name:       "nilness",
-	Doc:        "Annotates return values that will never be nil (typed or untyped)",
+	Doc:        "Annotates return values with their nilness",
 	Run:        run,
 	Requires:   []*analysis.Analyzer{buildir.Analyzer},
-	FactTypes:  []analysis.Fact{(*neverReturnsNilFact)(nil)},
+	FactTypes:  []analysis.Fact{(*nilnessFact)(nil)},
 	ResultType: reflect.TypeFor[*Result](),
 }
 
-// MayReturnNil reports whether the ret's return value of fn might be
-// a typed or untyped nil value. The value of ret is zero-based. When
-// globalOnly is true, the only possible nil values are global
-// variables.
-//
-// The analysis has false positives: MayReturnNil can incorrectly
-// report true, but never incorrectly reports false.
-func (r *Result) MayReturnNil(fn *types.Func, ret int) (yes bool, globalOnly bool) {
-	if !typeutil.IsPointerLike(fn.Type().(*types.Signature).Results().At(ret).Type()) {
-		return false, false
+// Nilness returns nilness information for return value ret of fn.
+func (r *Result) Nilness(fn *types.Func, ret int) ValueNilness {
+	typ := fn.Type().(*types.Signature).Results().At(ret).Type()
+	if !typeutil.IsPointerLike(typ) {
+		return ValueNilness{Outer: NeverNil}
 	}
 	if len(r.m[fn]) == 0 {
-		return true, false
+		return ValueNilness{Inner: MaybeNil, Outer: MaybeNil}
 	}
 
-	v := r.m[fn][ret]
-	return v != neverNil, v == onlyGlobal
+	return normalize(r.m[fn][ret], typ)
+}
+
+func normalize(v ValueNilness, typ types.Type) ValueNilness {
+	if v.Inner == 0 || !types.IsInterface(typ) {
+		v.Inner = MaybeNil
+	}
+	if v.Outer == 0 {
+		v.Outer = MaybeNil
+	}
+	return v
 }
 
 func run(pass *analysis.Pass) (any, error) {
 	seen := map[*ir.Function]struct{}{}
 	out := &Result{
-		m: map[*types.Func][]neverNilness{},
+		m: map[*types.Func][]ValueNilness{},
 	}
+
+	// TODO(dh): instead of recursion and giving up on mutual recursion, we
+	// should compute the DFA over the call graph, at least until we have
+	// proper function summaries.
 	for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
 		impl(pass, fn, seen)
 	}
 
 	for _, fact := range pass.AllObjectFacts() {
-		out.m[fact.Object.(*types.Func)] = fact.Fact.(*neverReturnsNilFact).Rets
+		out.m[fact.Object.(*types.Func)] = fact.Fact.(*nilnessFact).Rets
 	}
 
 	return out, nil
 }
 
-type neverNilness uint8
+type Nilness uint8
 
 const (
-	neverNil   neverNilness = 1
-	onlyGlobal neverNilness = 2
-	nilly      neverNilness = 3
+	// The value is never nil.
+	NeverNil Nilness = iota + 1
+	// The value is always nil.
+	AlwaysNil
+	// The value might be nil, but only because of the value of a global
+	// variable.
+	MaybeNilGlobal
+	// The value might be nil.
+	MaybeNil
 )
 
-func (n neverNilness) String() string {
+func (n Nilness) String() string {
 	switch n {
-	case neverNil:
-		return "never"
-	case onlyGlobal:
-		return "global"
-	case nilly:
-		return "nil"
+	case 0:
+		return "NoNilness"
+	case NeverNil:
+		return "NeverNil"
+	case AlwaysNil:
+		return "AlwaysNil"
+	case MaybeNilGlobal:
+		return "MaybeNilGlobal"
+	case MaybeNil:
+		return "MaybeNil"
 	default:
-		return "BUG"
+		return "InvalidNilness"
+	}
+}
+
+type state struct {
+	cloned bool
+	m      []ValueNilness
+	n      numbering
+}
+
+func (s *state) get(v ir.Value) ValueNilness {
+	if !typeutil.IsPointerLike(v.Type()) {
+		// All non-pointer-like types are always {_ NeverNil}.
+		return ValueNilness{Outer: NeverNil}
+	}
+	num := s.n.number(v)
+	if num < len(s.m) {
+		return s.m[num]
+	}
+
+	switch v.(type) {
+	case *ir.Parameter:
+		return ValueNilness{Inner: MaybeNil, Outer: MaybeNil}
+	case *ir.Builtin:
+		return ValueNilness{Outer: NeverNil}
+	case *ir.FreeVar:
+		return ValueNilness{Inner: MaybeNil, Outer: MaybeNil}
+	case *ir.Function:
+		return ValueNilness{Outer: NeverNil}
+	case *ir.Global:
+		// Globals are addresses, not the values stored in them. The addresses
+		// cannot be nil.
+		return ValueNilness{Outer: NeverNil}
+	}
+
+	return lattice{}.Ident()
+}
+
+func (s *state) set(key ir.Value, value ValueNilness) {
+	if !typeutil.IsPointerLike(key.Type()) {
+		// No point in recording state for non-pointer-like types. They're
+		// always {_ NeverNil}.
+		return
+	}
+
+	if value == (lattice{}.Ident()) {
+		// No point in storing the default value.
+		return
+	}
+	num := s.n.number(key)
+	if !s.cloned {
+		if num < len(s.m) && s.m[num] == value {
+			// Don't clone if the value already matches.
+			return
+		}
+		s.cloned = true
+		s.m = slices.Clone(s.m)
+	}
+	if num >= len(s.m) {
+		s.m = append(s.m, make([]ValueNilness, num-len(s.m)+1)...)
+	}
+	s.m[num] = value
+}
+
+func (s *state) setInner(key ir.Value, value Nilness) {
+	if !typeutil.IsPointerLike(key.Type()) {
+		return
+	}
+	if value == (lattice{}.Ident().Inner) {
+		return
+	}
+	num := s.n.number(key)
+	if !s.cloned {
+		if num < len(s.m) && s.m[num].Inner == value {
+			// Don't clone if the value already matches.
+			return
+		}
+		s.cloned = true
+		s.m = slices.Clone(s.m)
+	}
+	if num >= len(s.m) {
+		dflt := s.get(key)
+		s.m = append(s.m, make([]ValueNilness, num-len(s.m)+1)...)
+		s.m[num] = dflt
+	}
+	v := s.m[num]
+	v.Inner = value
+	s.m[num] = v
+}
+
+func (s *state) setOuter(key ir.Value, value Nilness) {
+	if !typeutil.IsPointerLike(key.Type()) {
+		return
+	}
+	if value == (lattice{}).Ident().Outer {
+		return
+	}
+	num := s.n.number(key)
+	if !s.cloned {
+		if num < len(s.m) && s.m[num].Outer == value {
+			// Don't clone if the value already matches.
+			return
+		}
+		s.cloned = true
+		s.m = slices.Clone(s.m)
+	}
+	if num >= len(s.m) {
+		dflt := s.get(key)
+		s.m = append(s.m, make([]ValueNilness, num-len(s.m)+1)...)
+		s.m[num] = dflt
 	}
+	v := s.m[num]
+	v.Outer = value
+	s.m[num] = v
 }
 
-func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) []neverNilness {
+func defaultNilnessForSignature(pass *analysis.Pass, typ *types.Signature) []ValueNilness {
+	n := typ.Results().Len()
+	if n == 0 {
+		return nil
+	}
+	out := make([]ValueNilness, n)
+	for i := range n {
+		out[i] = defaultNilness(pass, typ.Results().At(i).Type())
+	}
+	return out
+}
+
+func defaultNilness(pass *analysis.Pass, typ types.Type) ValueNilness {
+	if typeutil.IsPointerLike(typ) {
+		// IsPointerLike handles type parameters with type sets, too.
+		return ValueNilness{MaybeNil, MaybeNil}
+	} else {
+		return ValueNilness{NeverNil, NeverNil}
+	}
+}
+
+func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) []ValueNilness {
+	goto start
+bailout:
+	return defaultNilnessForSignature(pass, fn.Signature)
+
+start:
+	if fn.Signature.Results().Len() == 0 {
+		return nil
+	}
 	if fn.Object() == nil {
 		// TODO(dh): support closures
-		return nil
+		goto bailout
 	}
-	if fact := new(neverReturnsNilFact); pass.ImportObjectFact(fn.Object(), fact) {
+	if fact := new(nilnessFact); pass.ImportObjectFact(fn.Object(), fact) {
 		return fact.Rets
 	}
 	if fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {
-		return nil
+		goto bailout
 	}
 	if fn.Blocks == nil {
-		return nil
+		goto bailout
 	}
 	if _, ok := seenFns[fn]; ok {
 		// break recursion
-		return nil
+		goto bailout
 	}
 
 	seenFns[fn] = struct{}{}
 
-	seen := map[ir.Value]struct{}{}
-
-	var mightReturnNil func(v ir.Value) neverNilness
-	mightReturnNil = func(v ir.Value) neverNilness {
-		if _, ok := seen[v]; ok {
-			// break cycle
-			return nilly
-		}
-		if !typeutil.IsPointerLike(v.Type()) {
-			return neverNil
+	anyPointers := false
+	for ret := range fn.Signature.Results().Variables() {
+		if typeutil.IsPointerLike(ret.Type()) {
+			anyPointers = true
+			break
 		}
-		seen[v] = struct{}{}
-		switch v := v.(type) {
-		case *ir.MakeInterface:
-			return mightReturnNil(v.X)
-		case *ir.Convert:
-			return mightReturnNil(v.X)
-		case *ir.SliceToArrayPointer:
-			if typeutil.CoreType(v.Type()).(*types.Pointer).Elem().Underlying().(*types.Array).Len() == 0 {
-				return mightReturnNil(v.X)
-			} else {
-				// converting a slice to an array pointer of length > 0 panics if the slice is nil
-				return neverNil
-			}
-		case *ir.Slice:
-			return mightReturnNil(v.X)
-		case *ir.Phi:
-			ret := neverNil
-			for _, e := range v.Edges {
-				if n := mightReturnNil(e); n > ret {
-					ret = n
-				}
+	}
+
+	if !anyPointers {
+		goto bailout
+	}
+
+	n := numbering{}
+
+	processBlock := func(from, to *ir.BasicBlock, s state) state {
+		handleReturnValue := func(v ir.Value, call *ir.Call, idx int) {
+			typ := call.Common().Signature().Results().At(idx).Type()
+			if !typeutil.IsPointerLike(typ) {
+				s.setOuter(v, NeverNil)
+				return
 			}
-			return ret
-		case *ir.Extract:
-			switch d := v.Tuple.(type) {
-			case *ir.Call:
-				if callee := d.Call.StaticCallee(); callee != nil {
-					ret := impl(pass, callee, seenFns)
-					if len(ret) == 0 {
-						return nilly
+
+			if callee, ok := call.Call.Value.(*ir.Builtin); ok {
+				switch callee.Name() {
+				case "append":
+					// TODO(dh): if we knew that the varargs had non-zero
+					// length, we'd know that the resulting slice is non-nil.
+					switch an := s.get(call.Call.Args[0]).Outer; an {
+					case MaybeNil, MaybeNilGlobal, NeverNil:
+						s.setOuter(v, an)
+					case AlwaysNil:
+						s.setOuter(v, MaybeNil)
 					}
-					return ret[v.Index]
-				} else {
-					return nilly
-				}
-			case *ir.TypeAssert, *ir.Next, *ir.Select, *ir.MapLookup, *ir.TypeSwitch, *ir.Recv, *ir.Sigma:
-				// we don't need to look at the Extract's index
-				// because we've already checked its type.
-				return nilly
-			default:
-				panic(fmt.Sprintf("internal error: unhandled type %T", d))
-			}
-		case *ir.Call:
-			if callee := v.Call.StaticCallee(); callee != nil {
-				ret := impl(pass, callee, seenFns)
-				if len(ret) == 0 {
-					return nilly
+				case "UnsafeSlice":
+					// If len is negative, or if ptr is nil and len is not
+					// zero, unsafe.Slice panics. This implies that a non-nil
+					// pointer cannot become nil, and vice versa.
+					s.set(v, s.get(call.Call.Args[0]))
+				case "UnsafeStringData":
+					// TODO(dh): if we had string length information we could
+					// return better information.
+					s.setOuter(v, MaybeNil)
+				case "UnsafeSliceData":
+					// When the slice is non-nil but has zero capacity, the
+					// returned pointer is still non-nil, so we don't have to
+					// worry about that.
+					s.set(v, s.get(call.Call.Args[0]))
+				case "UnsafeAdd":
+					// TODO(dh): a positive addend can never result in a nil pointer.
+
+					// Pointer arithmetic can turn nil pointers into non-nil
+					// ones and vice versa.
+					s.setOuter(v, MaybeNil)
+				case "ssa:deferstack":
+					s.setOuter(v, NeverNil)
+				case "ssa:wrapnilchk":
+					s.setOuter(v, NeverNil)
+				case "recover":
+					s.setOuter(v, MaybeNil)
+				default:
+					panic(fmt.Sprintf("internal error: unhandled builtin %s", callee.Name()))
 				}
-				return ret[0]
-			} else {
-				return nilly
+				return
 			}
-		case *ir.BinOp, *ir.UnOp, *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr, *ir.Global, *ir.MakeSlice, *ir.MakeClosure, *ir.Function, *ir.MakeMap, *ir.MakeChan:
-			return neverNil
-		case *ir.Sigma:
-			iff, ok := v.From.Control().(*ir.If)
-			if !ok {
-				return nilly
+
+			callee := call.Common().StaticCallee()
+			if callee == nil {
+				// We don't know which function is being called.
+				s.set(v, ValueNilness{MaybeNil, MaybeNil})
+				return
 			}
-			binop, ok := iff.Cond.(*ir.BinOp)
-			if !ok {
-				return nilly
+			calleeNilness := impl(pass, callee, seenFns)
+			if len(calleeNilness) > idx {
+				s.set(v, normalize(calleeNilness[idx], typ))
+			} else {
+				s.set(v, ValueNilness{MaybeNil, MaybeNil})
 			}
-			isNil := func(v ir.Value) bool {
-				k, ok := v.(*ir.Const)
-				if !ok {
+		}
+
+		for _, instr := range from.Instrs {
+			// It is tempting to return early when instr is an ir.Value that
+			// doesn't have pointer type. However, instructions like ir.Load
+			// tell us something about the value being operated on.
+
+			switch v := instr.(type) {
+			case *ir.Convert:
+				s.set(v, s.get(v.X))
+			case *ir.SliceToArrayPointer:
+				// Go does not currently allow (*T)(s) where T is a type
+				// parameter with a type set consisting of array types, but it
+				// does allow (T)(s) where T is a type parameter with a type
+				// set consisting of pointers to array types.
+
+				allNonZero := typeutil.All(v.Type(), func(term *types.Term) bool {
+					ptr := term.Type().Underlying().(*types.Pointer).Elem()
+					return typeutil.All(ptr, func(innerTerm *types.Term) bool {
+						return innerTerm.Type().Underlying().(*types.Array).Len() != 0
+					})
+				})
+
+				if allNonZero {
+					// converting a slice to an array pointer of length > 0
+					// panics if the slice is nil
+					s.setOuter(v, NeverNil)
+					s.setOuter(v.X, NeverNil)
+				} else {
+					s.set(v, s.get(v.X))
+				}
+			case *ir.SliceToArray:
+				// Pretty much the same logic as SliceToArrayPointer, minus the
+				// pointer.
+
+				allNonZero := typeutil.All(v.Type(), func(term *types.Term) bool {
+					return term.Type().Underlying().(*types.Array).Len() != 0
+				})
+
+				if allNonZero {
+					// converting a slice to an array of length > 0
+					// panics if the slice is nil
+					s.setOuter(v.X, NeverNil)
+				}
+			case *ir.Slice:
+				if typeutil.All(v.X.Type(), typeutil.IsType[*types.Array]) {
+					// Slicing arrays never results in a nil slice.
+					s.setOuter(v, NeverNil)
+					continue
+				}
+
+				// checkBound returns true if one of the bounds (low, high,
+				// capacity) has non-zero value.
+				checkBound := func(v ir.Value) bool {
+					if v == nil {
+						return false
+					}
+					// TODO(dh): this is where integration with constant
+					// propagation and value range analysis would be useful.
+					if k, ok := v.(*ir.Const); ok {
+						kv, ok := constant.Int64Val(k.Value)
+						return !ok || kv != 0
+					}
 					return false
 				}
-				return k.Value == nil
-			}
-			if binop.X == v.X && isNil(binop.Y) || binop.Y == v.X && isNil(binop.X) {
+				if checkBound(v.Low) || checkBound(v.High) || checkBound(v.Max) {
+					// One of the indices is non-zero, which means slicing can
+					// only succeed if the slicee is not nil.
+					s.setOuter(v, NeverNil)
+					s.setOuter(v.X, NeverNil)
+				} else {
+					// The new slice is as nilly as the slicee.
+					s.set(v, s.get(v.X))
+				}
+
+			case *ir.If:
+				cond := v.Cond
+				binop, ok := cond.(*ir.BinOp)
+				if !ok {
+					continue
+				}
+				isNil := func(v ir.Value) bool {
+					k, ok := v.(*ir.Const)
+					if !ok {
+						return false
+					}
+					return k.Value == nil
+				}
+				var target ir.Value
+				if isNil(binop.X) {
+					target = binop.Y
+				} else if isNil(binop.Y) {
+					target = binop.X
+				} else {
+					continue
+				}
 				op := binop.Op
-				if v.From.Succs[0] != v.Block() {
+				if to != from.Succs[0] {
 					// we're in the false branch, negate op
 					switch op {
 					case token.EQL:
@@ -211,45 +487,344 @@ func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{
 				}
 				switch op {
 				case token.EQL:
-					return nilly
+					s.set(target, ValueNilness{AlwaysNil, AlwaysNil})
 				case token.NEQ:
-					return neverNil
+					s.setOuter(target, NeverNil)
 				default:
 					panic(fmt.Sprintf("internal error: unhandled token %v", op))
 				}
+
+				// TODO(dh): also handle comparison of two non-nil values. The
+				// true branch of neverNil == nilly makes the nilly value neverNil.
+			case *ir.ChangeType:
+				s.set(v, s.get(v.X))
+			case *ir.MultiConvert:
+				s.set(v, s.get(v.X))
+			case *ir.Load:
+				if _, ok := v.X.(*ir.Global); ok {
+					s.setOuter(v, MaybeNilGlobal)
+				} else {
+					s.setOuter(v, MaybeNil)
+				}
+				s.setOuter(v.X, NeverNil)
+			case *ir.FieldAddr:
+				s.setOuter(v.X, NeverNil)
+				s.setOuter(v, NeverNil)
+			case *ir.IndexAddr:
+				s.setOuter(v.X, NeverNil)
+				s.setOuter(v, NeverNil)
+			case *ir.Alloc, *ir.MakeMap, *ir.MakeSlice, *ir.MakeClosure, *ir.MakeChan:
+				s.setOuter(v.(ir.Value), NeverNil)
+			case *ir.MapUpdate:
+				s.setOuter(v.Map, NeverNil)
+			case *ir.Store:
+				s.setOuter(v.Addr, NeverNil)
+			case ir.CallInstruction:
+				// go/defer/calling a nil function fatals/panics
+				if !v.Common().IsInvoke() {
+					s.setOuter(v.Common().Value, NeverNil)
+				}
+				_, ok := v.(*ir.Call)
+				if !ok {
+					// Defer and Go don't produce values
+					continue
+				}
+				if v.Common().Signature().Results().Len() != 1 {
+					// If the called function doesn't return any values then we
+					// don't care about it. If it has more than one return
+					// value, they'll be handled by Extract.
+					continue
+				}
+
+				handleReturnValue(v.(ir.Value), v.(*ir.Call), 0)
+			case *ir.Send:
+				s.setOuter(v.Chan, NeverNil)
+			case *ir.Recv:
+				s.setOuter(v.Chan, NeverNil)
+				s.set(v, ValueNilness{MaybeNil, MaybeNil})
+			case *ir.MakeInterface:
+				s.set(v, ValueNilness{
+					Inner: s.get(v.X).Outer,
+					Outer: NeverNil,
+				})
+			case *ir.ChangeInterface:
+				s.set(v, s.get(v.X))
+			case *ir.TypeAssert:
+				if !v.CommaOk {
+					// The interface value cannot have been nil, or the type
+					// assertion would have panicked.
+					s.setOuter(v.X, NeverNil)
+
+					if types.IsInterface(v.Type()) && !typeparams.IsTypeParam(v.Type()) {
+						// Type asserting to another interface doesn't succeed
+						// if the assertee was nil. It also results in a new
+						// interface value.
+						s.setOuter(v, NeverNil)
+						s.setInner(v, s.get(v.X).Inner)
+					} else {
+						// We've extracted the interface value's inner value.
+						s.setOuter(v, s.get(v.X).Inner)
+					}
+				} else {
+					// In a comma-ok type assertion, the return type is a
+					// tuple. There'll be Extract instructions getting the
+					// individual values, to which we'll attach the nilness
+					// info.
+				}
+			case *ir.TypeSwitch:
+				// Handled in Extract
+			case *ir.MapLookup:
+				if s.get(v.X).Outer == AlwaysNil {
+					s.set(v, ValueNilness{AlwaysNil, AlwaysNil})
+				} else {
+					s.set(v, ValueNilness{MaybeNil, MaybeNil})
+				}
+			case *ir.Field:
+				s.set(v.X, ValueNilness{NeverNil, NeverNil})
+				s.set(v, ValueNilness{MaybeNil, MaybeNil})
+			case *ir.Index:
+				s.set(v.X, ValueNilness{NeverNil, NeverNil})
+				s.set(v, ValueNilness{MaybeNil, MaybeNil})
+
+			case *ir.Extract:
+				switch tuple := v.Tuple.(type) {
+				case *ir.TypeAssert:
+					// When we get here, the type assertion used the comma-ok
+					// form, and we don't yet know anything about the result of
+					// the type assertion.
+					if v.Index == 0 {
+						s.set(v, ValueNilness{MaybeNil, MaybeNil})
+					}
+
+					// TODO(dh): We should set v's nilness in the true and
+					// false branches of checks on the ok value. However, ok can be
+					// used in arbitrary ways, and we're also not set up to handle
+					// relational facts (ok being true or false affects the value
+					// of the other Extract).
+
+				case *ir.Call:
+					handleReturnValue(v, tuple, v.Index)
+
+				case *ir.TypeSwitch:
+					if v.Index == 0 {
+						// Index 0 is an integer and not interesting.
+						continue
+					}
+					idx := v.Index - 1
+					if idx >= len(tuple.Conds) {
+						// Default branch
+
+						// If there is an untyped nil case, then being in the
+						// default branch tells us that the interface value
+						// isn't nil.
+						hasNil := slices.ContainsFunc(tuple.Conds, func(typ types.Type) bool {
+							if typ, ok := typ.(*types.Basic); ok && typ.Kind() == types.UntypedNil {
+								return true
+							}
+							return false
+						})
+						if hasNil {
+							s.setOuter(tuple.Tag, NeverNil)
+						} else {
+							s.setOuter(tuple.Tag, MaybeNil)
+						}
+						s.setOuter(v, s.get(tuple.Tag).Inner)
+					} else {
+						// There is no Extract for the 'untyped nil' case,
+						// which means that executing any Extract from a type
+						// switch implies that the switched-over value wasn't a
+						// nil interface value.
+						s.setOuter(tuple.Tag, NeverNil)
+						typ := tuple.Conds[idx]
+						if types.IsInterface(typ) && !typeparams.IsTypeParam(typ) {
+							// Succesfully type asserting to an interface type
+							// always produces a non-nil interface value.
+							s.setInner(v, s.get(tuple.Tag).Inner)
+							s.setOuter(v, NeverNil)
+						} else {
+							s.setOuter(v, s.get(tuple.Tag).Inner)
+						}
+					}
+				default:
+					s.set(v, ValueNilness{MaybeNil, MaybeNil})
+				}
+			case *ir.Select:
+				if v.Blocking && len(v.States) == 1 {
+					// If the select doesn't have a default branch and only has
+					// one state, that state's channel cannot have been nil if
+					// we finished execution the select.
+					s.setOuter(v.States[0].Chan, NeverNil)
+				}
+			case *ir.Jump, *ir.BlankStore, *ir.Phi,
+				*ir.Panic, *ir.Return, *ir.RunDefers, *ir.Unreachable, *ir.ConstantSwitch,
+				*ir.UnOp, *ir.BinOp, *ir.CompositeValue, *ir.Range, *ir.Next:
+			default:
+				posn := pass.Fset.PositionFor(v.Pos(), false)
+				panic(fmt.Sprintf("internal error: unhandled type %T at %s", v, posn))
 			}
-			return nilly
-		case *ir.ChangeType:
-			return mightReturnNil(v.X)
-		case *ir.MultiConvert:
-			return mightReturnNil(v.X)
-		case *ir.Load:
-			if _, ok := v.X.(*ir.Global); ok {
-				return onlyGlobal
+		}
+		return s
+	}
+
+	processPhis := func(b *ir.BasicBlock, i int, s state) state {
+		for _, instr := range b.Instrs {
+			if instr, ok := instr.(*ir.Phi); ok {
+				s.set(instr, s.get(instr.Edges[i]))
+			} else {
+				break
 			}
-			return nilly
-		case *ir.AggregateConst:
-			return neverNil
-		case *ir.TypeAssert, *ir.ChangeInterface, *ir.Field, *ir.Const, *ir.GenericConst, *ir.Index, *ir.MapLookup, *ir.Parameter, *ir.Recv, *ir.TypeSwitch:
-			return nilly
-		default:
-			panic(fmt.Sprintf("internal error: unhandled type %T", v))
 		}
+		return s
 	}
-	ret := fn.Exit.Control().(*ir.Return)
-	out := make([]neverNilness, len(ret.Results))
-	export := false
-	for i, v := range ret.Results {
-		// OPT(dh): couldn't we check the result type's pointer-likeness early, and skip
-		// processing the return value altogether?
-		v := mightReturnNil(v)
-		out[i] = v
-		if v != nilly && typeutil.IsPointerLike(fn.Signature.Results().At(i).Type()) {
-			export = true
+
+	// Populate default state for non-instruction values we encounter. We
+	// cannot defer this logic to state.get because control flow merges use
+	// simple merges of lattice values and won't know about value-specific
+	// defaults.
+	entrys := state{cloned: true, n: n}
+	for _, param := range fn.Params {
+		if typeutil.IsPointerLike(param.Type()) {
+			entrys.set(param, ValueNilness{Inner: MaybeNil, Outer: MaybeNil})
+		} else {
+			// We never track nilness for value types, so they don't have to be
+			// present in the entry state, either.
 		}
 	}
-	if export {
-		pass.ExportObjectFact(fn.Object(), &neverReturnsNilFact{out})
+	if strings.HasPrefix(fn.Synthetic, "bound method wrapper") {
+		// This is a bound method and the bound receiver might be nil
+		for _, fvar := range fn.FreeVars {
+			entrys.set(fvar, ValueNilness{Outer: MaybeNil})
+		}
+	} else {
+		// This is a closure, and closed over variables are allocs, which
+		// cannot be nil.
+		for _, fvar := range fn.FreeVars {
+			entrys.set(fvar, ValueNilness{Outer: NeverNil})
+		}
 	}
-	return out
+	var ops []*ir.Value
+	for _, b := range fn.Blocks {
+		for _, instr := range b.Instrs {
+			ops = instr.Operands(ops[:0])
+			for _, pop := range ops {
+				if op, ok := (*pop).(*ir.Const); ok && typeutil.IsPointerLike(op.Type()) {
+					// The only constant pointer-like is nil.
+					entrys.set(op, ValueNilness{Inner: AlwaysNil, Outer: AlwaysNil})
+				}
+			}
+		}
+	}
+	res := dense.Forward[dfa.DenseMapLattice[ValueNilness, lattice]](
+		fn,
+		map[int][]ValueNilness{0: entrys.m},
+		func(fromID, toID int, in []ValueNilness) []ValueNilness {
+			from := fn.Blocks[fromID]
+			to := fn.Blocks[toID]
+			s := state{n: n, m: in}
+			s = processBlock(from, to, s)
+			i := slices.Index(to.Preds, from)
+			s = processPhis(to, i, s)
+			return s.m
+		},
+	)
+
+	retNilness := make([]ValueNilness, fn.Signature.Results().Len())
+	for b := range fn.Returns() {
+		ret := b.Control().(*ir.Return)
+		s := state{n: n, m: res.In(b.Index)}
+		s = processBlock(b, nil, s)
+		for i, res := range ret.Results {
+			retNilness[i] = lattice{}.Merge(retNilness[i], s.get(res))
+		}
+	}
+
+	interesting := false
+	for i := range retNilness {
+		typ := fn.Signature.Results().At(i).Type()
+		if !typeutil.IsPointerLike(typ) {
+			retNilness[i] = ValueNilness{NeverNil, NeverNil}
+			continue
+		}
+		retNilness[i] = normalize(retNilness[i], typ)
+		if retNilness[i] != (ValueNilness{MaybeNil, MaybeNil}) {
+			interesting = true
+		}
+	}
+
+	if interesting {
+		pass.ExportObjectFact(fn.Object(), &nilnessFact{retNilness})
+	}
+
+	return retNilness
+}
+
+type lattice struct{}
+
+var _ dfa.Semilattice[ValueNilness] = lattice{}
+
+// Equals implements [dfa.Semilattice].
+func (l lattice) Equals(a, b ValueNilness) bool {
+	return a == b
+}
+
+// Ident implements [dfa.Semilattice].
+func (l lattice) Ident() ValueNilness {
+	return ValueNilness{}
+}
+
+var latticeMerge = [5][5]Nilness{
+	0: {
+		0:              0,
+		NeverNil:       NeverNil,
+		AlwaysNil:      AlwaysNil,
+		MaybeNilGlobal: MaybeNilGlobal,
+		MaybeNil:       MaybeNil,
+	},
+	NeverNil: {
+		0:              NeverNil,
+		NeverNil:       NeverNil,
+		AlwaysNil:      MaybeNil,
+		MaybeNilGlobal: MaybeNilGlobal,
+		MaybeNil:       MaybeNil,
+	},
+	AlwaysNil: {
+		0:              AlwaysNil,
+		NeverNil:       MaybeNil,
+		AlwaysNil:      AlwaysNil,
+		MaybeNilGlobal: MaybeNil,
+		MaybeNil:       MaybeNil,
+	},
+	MaybeNilGlobal: {
+		0:              MaybeNilGlobal,
+		NeverNil:       MaybeNilGlobal,
+		AlwaysNil:      MaybeNil,
+		MaybeNilGlobal: MaybeNilGlobal,
+		MaybeNil:       MaybeNil,
+	},
+	MaybeNil: {
+		0:              MaybeNil,
+		NeverNil:       MaybeNil,
+		AlwaysNil:      MaybeNil,
+		MaybeNilGlobal: MaybeNil,
+		MaybeNil:       MaybeNil,
+	},
+}
+
+// Merge implements [dfa.Semilattice].
+func (l lattice) Merge(a, b ValueNilness) ValueNilness {
+	return ValueNilness{
+		Inner: latticeMerge[a.Inner][b.Inner],
+		Outer: latticeMerge[a.Outer][b.Outer],
+	}
+}
+
+type numbering map[ir.Value]int
+
+func (n numbering) number(v ir.Value) int {
+	i, ok := n[v]
+	if !ok {
+		i = len(n)
+		n[v] = i
+	}
+	return i
 }
diff --git a/vendor/honnef.co/go/tools/analysis/facts/typedness/typedness.go b/vendor/honnef.co/go/tools/analysis/facts/typedness/typedness.go
deleted file mode 100644
index fd21dd22..00000000
--- a/vendor/honnef.co/go/tools/analysis/facts/typedness/typedness.go
+++ /dev/null
@@ -1,253 +0,0 @@
-package typedness
-
-import (
-	"fmt"
-	"go/token"
-	"go/types"
-	"reflect"
-
-	"honnef.co/go/tools/go/ir"
-	"honnef.co/go/tools/go/ir/irutil"
-	"honnef.co/go/tools/internal/passes/buildir"
-
-	"golang.org/x/exp/typeparams"
-	"golang.org/x/tools/go/analysis"
-)
-
-// alwaysTypedFact denotes that a function's return value will never
-// be untyped nil. The analysis errs on the side of false negatives.
-type alwaysTypedFact struct {
-	Rets uint8
-}
-
-func (*alwaysTypedFact) AFact() {}
-func (fact *alwaysTypedFact) String() string {
-	return fmt.Sprintf("always typed: %08b", fact.Rets)
-}
-
-type Result struct {
-	m map[*types.Func]uint8
-}
-
-var Analysis = &analysis.Analyzer{
-	Name:       "typedness",
-	Doc:        "Annotates return values that are always typed values",
-	Run:        run,
-	Requires:   []*analysis.Analyzer{buildir.Analyzer},
-	FactTypes:  []analysis.Fact{(*alwaysTypedFact)(nil)},
-	ResultType: reflect.TypeFor[*Result](),
-}
-
-// MustReturnTyped reports whether the ret's return value of fn must
-// be a typed value, i.e. an interface value containing a concrete
-// type or trivially a concrete type. The value of ret is zero-based.
-//
-// The analysis has false negatives: MustReturnTyped may incorrectly
-// report false, but never incorrectly reports true.
-func (r *Result) MustReturnTyped(fn *types.Func, ret int) bool {
-	if _, ok := fn.Type().(*types.Signature).Results().At(ret).Type().Underlying().(*types.Interface); !ok {
-		return true
-	}
-	return (r.m[fn] & (1 << ret)) != 0
-}
-
-func run(pass *analysis.Pass) (any, error) {
-	seen := map[*ir.Function]struct{}{}
-	out := &Result{
-		m: map[*types.Func]uint8{},
-	}
-	for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
-		impl(pass, fn, seen)
-	}
-
-	for _, fact := range pass.AllObjectFacts() {
-		out.m[fact.Object.(*types.Func)] = fact.Fact.(*alwaysTypedFact).Rets
-	}
-
-	return out, nil
-}
-
-func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) (out uint8) {
-	if fn.Signature.Results().Len() > 8 {
-		return 0
-	}
-	if fn.Object() == nil {
-		// TODO(dh): support closures
-		return 0
-	}
-	if fact := new(alwaysTypedFact); pass.ImportObjectFact(fn.Object(), fact) {
-		return fact.Rets
-	}
-	if fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {
-		return 0
-	}
-	if fn.Blocks == nil {
-		return 0
-	}
-	if irutil.IsStub(fn) {
-		return 0
-	}
-	if _, ok := seenFns[fn]; ok {
-		// break recursion
-		return 0
-	}
-
-	seenFns[fn] = struct{}{}
-	defer func() {
-		for i := 0; i < fn.Signature.Results().Len(); i++ {
-			if _, ok := fn.Signature.Results().At(i).Type().Underlying().(*types.Interface); !ok {
-				// we don't need facts to know that non-interface
-				// types can't be untyped nil. zeroing out those bits
-				// may result in all bits being zero, in which case we
-				// don't have to save any fact.
-				out &= ^(1 << i)
-			}
-		}
-		if out > 0 {
-			pass.ExportObjectFact(fn.Object(), &alwaysTypedFact{out})
-		}
-	}()
-
-	isUntypedNil := func(v ir.Value) bool {
-		k, ok := v.(*ir.Const)
-		if !ok {
-			return false
-		}
-		if _, ok := k.Type().Underlying().(*types.Interface); !ok {
-			return false
-		}
-		return k.Value == nil
-	}
-
-	var do func(v ir.Value, seen map[ir.Value]struct{}) bool
-	do = func(v ir.Value, seen map[ir.Value]struct{}) bool {
-		if _, ok := seen[v]; ok {
-			// break cycle
-			return false
-		}
-		seen[v] = struct{}{}
-		switch v := v.(type) {
-		case *ir.Const:
-			// can't be a typed nil, because then we'd be returning the
-			// result of MakeInterface.
-			return false
-		case *ir.ChangeInterface:
-			return do(v.X, seen)
-		case *ir.Extract:
-			call, ok := v.Tuple.(*ir.Call)
-			if !ok {
-				// We only care about extracts of function results. For
-				// everything else (e.g. channel receives and map
-				// lookups), we can either not deduce any information, or
-				// will see a MakeInterface.
-				return false
-			}
-			if callee := call.Call.StaticCallee(); callee != nil {
-				return impl(pass, callee, seenFns)&(1<interface conversions, which
-			// don't tell us anything about the nilness.
-			return false
-		case *ir.MapLookup, *ir.Index, *ir.Recv, *ir.Parameter, *ir.Load, *ir.Field:
-			// All other instructions that tell us nothing about the
-			// typedness of interface values.
-			return false
-		default:
-			panic(fmt.Sprintf("internal error: unhandled type %T", v))
-		}
-	}
-
-	ret := fn.Exit.Control().(*ir.Return)
-	for i, v := range ret.Results {
-		typ := fn.Signature.Results().At(i).Type()
-		if _, ok := typ.Underlying().(*types.Interface); ok && !typeparams.IsTypeParam(typ) {
-			if do(v, map[ir.Value]struct{}{}) {
-				out |= 1 << i
-			}
-		}
-	}
-	return out
-}
diff --git a/vendor/honnef.co/go/tools/analysis/report/report.go b/vendor/honnef.co/go/tools/analysis/report/report.go
index 82befcae..39919a89 100644
--- a/vendor/honnef.co/go/tools/analysis/report/report.go
+++ b/vendor/honnef.co/go/tools/analysis/report/report.go
@@ -13,7 +13,6 @@ import (
 
 	"honnef.co/go/tools/analysis/code"
 	"honnef.co/go/tools/analysis/facts/generated"
-	"honnef.co/go/tools/go/ast/astutil"
 
 	"golang.org/x/tools/go/analysis"
 )
@@ -122,7 +121,7 @@ func shortRange(node ast.Node) (pos, end token.Pos) {
 	case *ast.FuncLit:
 		return node.Pos(), node.Type.End()
 	case *ast.GoStmt:
-		if _, ok := astutil.Unparen(node.Call.Fun).(*ast.FuncLit); ok {
+		if _, ok := ast.Unparen(node.Call.Fun).(*ast.FuncLit); ok {
 			return node.Pos(), node.Go + token.Pos(len("go"))
 		} else {
 			return node.Pos(), node.End()
diff --git a/vendor/honnef.co/go/tools/go/ast/astutil/util.go b/vendor/honnef.co/go/tools/go/ast/astutil/util.go
index 34265440..c23968f8 100644
--- a/vendor/honnef.co/go/tools/go/ast/astutil/util.go
+++ b/vendor/honnef.co/go/tools/go/ast/astutil/util.go
@@ -69,17 +69,6 @@ func GroupSpecs(fset *token.FileSet, specs []ast.Spec) [][]ast.Spec {
 	return groups
 }
 
-// Unparen returns e with any enclosing parentheses stripped.
-func Unparen(e ast.Expr) ast.Expr {
-	for {
-		p, ok := e.(*ast.ParenExpr)
-		if !ok {
-			return e
-		}
-		e = p.X
-	}
-}
-
 // CopyExpr creates a deep copy of an expression.
 // It doesn't support copying FuncLits and returns ok == false when encountering one.
 func CopyExpr(node ast.Expr) (ast.Expr, bool) {
diff --git a/vendor/honnef.co/go/tools/go/ir/UPSTREAM b/vendor/honnef.co/go/tools/go/ir/UPSTREAM
deleted file mode 100644
index 03f487ae..00000000
--- a/vendor/honnef.co/go/tools/go/ir/UPSTREAM
+++ /dev/null
@@ -1,9 +0,0 @@
-This package started as a copy of golang.org/x/tools/go/ssa, imported from an unknown commit in 2016.
-It has since been heavily modified to match our own needs in an IR.
-The changes are too many to list here, and it is best to consider this package independent of go/ssa.
-
-Upstream changes still get applied when they address bugs in portions of code we have inherited.
-
-The last upstream commit we've looked at was:
-05409620da166985e94b711ad4103bee40406eee
-
diff --git a/vendor/honnef.co/go/tools/go/ir/block.go b/vendor/honnef.co/go/tools/go/ir/block.go
new file mode 100644
index 00000000..44399c7a
--- /dev/null
+++ b/vendor/honnef.co/go/tools/go/ir/block.go
@@ -0,0 +1,133 @@
+// 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 ir
+
+import (
+	"fmt"
+	"go/ast"
+)
+
+// This file implements the BasicBlock type.
+
+// addEdge adds a control-flow graph edge from from to to.
+func addEdge(from, to *BasicBlock) {
+	from.Succs = append(from.Succs, to)
+	to.Preds = append(to.Preds, from)
+}
+
+// Control returns the last instruction in the block.
+func (b *BasicBlock) Control() Instruction {
+	if len(b.Instrs) == 0 {
+		return nil
+	}
+	return b.Instrs[len(b.Instrs)-1]
+}
+
+// Parent returns the function that contains block b.
+func (b *BasicBlock) Parent() *Function { return b.parent }
+
+func (b *BasicBlock) Reaches(o *BasicBlock) bool {
+	return b.SCC.reachable.Bit(o.SCC.Index) != 0
+}
+
+// String returns a human-readable label of this block.
+// It is not guaranteed unique within the function.
+func (b *BasicBlock) String() string {
+	return fmt.Sprintf("%d", b.Index)
+}
+
+// emit appends an instruction to the current basic block.
+// If the instruction defines a Value, it is returned.
+func (b *BasicBlock) emit(i Instruction, source ast.Node) Value {
+	i.setSource(source)
+	i.setBlock(b)
+	b.Instrs = append(b.Instrs, i)
+	v, _ := i.(Value)
+	return v
+}
+
+// predIndex returns the i such that b.Preds[i] == c or panics if
+// there is none.
+func (b *BasicBlock) predIndex(c *BasicBlock) int {
+	for i, pred := range b.Preds {
+		if pred == c {
+			return i
+		}
+	}
+	panic(fmt.Sprintf("no edge %s -> %s", c, b))
+}
+
+// hasPhi returns true if b.Instrs contains φ-nodes.
+func (b *BasicBlock) hasPhi() bool {
+	_, ok := b.Instrs[0].(*Phi)
+	return ok
+}
+
+func (b *BasicBlock) Phis() []Instruction {
+	return b.phis()
+}
+
+// phis returns the prefix of b.Instrs containing all the block's φ-nodes.
+func (b *BasicBlock) phis() []Instruction {
+	for i, instr := range b.Instrs {
+		if _, ok := instr.(*Phi); !ok {
+			return b.Instrs[:i]
+		}
+	}
+	return nil // unreachable in well-formed blocks
+}
+
+// replacePred replaces all occurrences of p in b's predecessor list with q.
+// Ordinarily there should be at most one.
+func (b *BasicBlock) replacePred(p, q *BasicBlock) {
+	for i, pred := range b.Preds {
+		if pred == p {
+			b.Preds[i] = q
+		}
+	}
+}
+
+// replaceSucc replaces all occurrences of p in b's successor list with q.
+// Ordinarily there should be at most one.
+func (b *BasicBlock) replaceSucc(p, q *BasicBlock) {
+	for i, succ := range b.Succs {
+		if succ == p {
+			b.Succs[i] = q
+		}
+	}
+}
+
+// removePred removes all occurrences of p in b's
+// predecessor list and φ-nodes.
+// Ordinarily there should be at most one.
+func (b *BasicBlock) removePred(p *BasicBlock) {
+	phis := b.phis()
+
+	// We must preserve edge order for φ-nodes.
+	j := 0
+	for i, pred := range b.Preds {
+		if pred != p {
+			b.Preds[j] = b.Preds[i]
+			// Strike out φ-edge too.
+			for _, instr := range phis {
+				phi := instr.(*Phi)
+				phi.Edges[j] = phi.Edges[i]
+			}
+			j++
+		}
+	}
+	// Nil out b.Preds[j:] and φ-edges[j:] to aid GC.
+	for i := j; i < len(b.Preds); i++ {
+		b.Preds[i] = nil
+		for _, instr := range phis {
+			instr.(*Phi).Edges[i] = nil
+		}
+	}
+	b.Preds = b.Preds[:j]
+	for _, instr := range phis {
+		phi := instr.(*Phi)
+		phi.Edges = phi.Edges[:j]
+	}
+}
diff --git a/vendor/honnef.co/go/tools/go/ir/blockopt.go b/vendor/honnef.co/go/tools/go/ir/blockopt.go
index 53788611..de6ac9a7 100644
--- a/vendor/honnef.co/go/tools/go/ir/blockopt.go
+++ b/vendor/honnef.co/go/tools/go/ir/blockopt.go
@@ -38,12 +38,9 @@ func deleteUnreachableBlocks(f *Function) {
 		b.gaps = white
 	}
 	markReachable(f.Blocks[0])
-	// In SSI form, we need the exit to be reachable for correct
-	// post-dominance information. In original form, however, we
-	// cannot unconditionally mark it reachable because we won't
-	// be adding fake edges, and this breaks the calculation of
-	// dominance information.
-	markReachable(f.Exit)
+	if f.Recover != nil {
+		markReachable(f.Recover)
+	}
 	for i, b := range f.Blocks {
 		if b.gaps == white {
 			for _, c := range b.Succs {
@@ -67,7 +64,7 @@ func jumpThreading(f *Function, b *BasicBlock) bool {
 	if b.Index == 0 {
 		return false // don't apply to entry block
 	}
-	if b.Instrs == nil {
+	if len(b.Instrs) == 0 {
 		return false
 	}
 	for _, pred := range b.Preds {
@@ -120,9 +117,6 @@ func fuseBlocks(f *Function, a *BasicBlock) bool {
 	if len(a.Succs) != 1 {
 		return false
 	}
-	if a.Succs[0] == f.Exit {
-		return false
-	}
 	b := a.Succs[0]
 	if len(b.Preds) != 1 {
 		return false
@@ -165,11 +159,6 @@ func fuseBlocks(f *Function, a *BasicBlock) bool {
 // completed function: dead block elimination, block fusion, jump
 // threading.
 func optimizeBlocks(f *Function) {
-	if debugBlockOpt {
-		f.WriteTo(os.Stderr)
-		mustSanityCheck(f, nil)
-	}
-
 	deleteUnreachableBlocks(f)
 
 	// Loop until no further progress.
diff --git a/vendor/honnef.co/go/tools/go/ir/builder.go b/vendor/honnef.co/go/tools/go/ir/builder.go
index 17a6c891..d2918304 100644
--- a/vendor/honnef.co/go/tools/go/ir/builder.go
+++ b/vendor/honnef.co/go/tools/go/ir/builder.go
@@ -4,25 +4,73 @@
 
 package ir
 
-// This file implements the BUILD phase of IR construction.
+// This file defines the builder, which builds SSA-form IR for function bodies.
 //
-// IR construction has two phases, CREATE and BUILD.  In the CREATE phase
-// (create.go), all packages are constructed and type-checked and
-// definitions of all package members are created, method-sets are
-// computed, and wrapper methods are synthesized.
-// ir.Packages are created in arbitrary order.
+// SSA construction has two phases, "create" and "build". First, one
+// or more packages are created in any order by a sequence of calls to
+// CreatePackage, either from syntax or from mere type information.
+// Each created package has a complete set of Members (const, var,
+// type, func) that can be accessed through methods like
+// Program.FuncValue.
 //
-// In the BUILD phase (builder.go), the builder traverses the AST of
-// each Go source function and generates IR instructions for the
-// function body.  Initializer expressions for package-level variables
-// are emitted to the package's init() function in the order specified
-// by go/types.Info.InitOrder, then code for each function in the
-// package is generated in lexical order.
+// It is not necessary to call CreatePackage for all dependencies of
+// each syntax package, only for its direct imports. (In future
+// perhaps even this restriction may be lifted.)
 //
-// The builder's and Program's indices (maps) are populated and
-// mutated during the CREATE phase, but during the BUILD phase they
-// remain constant.  The sole exception is Prog.methodSets and its
-// related maps, which are protected by a dedicated mutex.
+// Second, packages created from syntax are built, by one or more
+// calls to Package.Build, which may be concurrent; or by a call to
+// Program.Build, which builds all packages in parallel. Building
+// traverses the type-annotated syntax tree of each function body and
+// creates SSA-form IR, a control-flow graph of instructions,
+// populating fields such as Function.Body, .Params, and others.
+//
+// Building may create additional methods, including:
+// - wrapper methods (e.g. for embedding, or implicit &recv)
+// - bound method closures (e.g. for use(recv.f))
+// - thunks (e.g. for use(I.f) or use(T.f))
+// - generic instances (e.g. to produce f[int] from f[any]).
+// As these methods are created, they are added to the build queue,
+// and then processed in turn, until a fixed point is reached,
+// Since these methods might belong to packages that were not
+// created (by a call to CreatePackage), their Pkg field is unset.
+//
+// Instances of generic functions may be either instantiated (f[int]
+// is a copy of f[T] with substitutions) or wrapped (f[int] delegates
+// to f[T]), depending on the availability of generic syntax and the
+// InstantiateGenerics mode flag.
+//
+// Each package has an initializer function named "init" that calls
+// the initializer functions of each direct import, computes and
+// assigns the initial value of each global variable, and calls each
+// source-level function named "init". (These generate SSA functions
+// named "init#1", "init#2", etc.)
+//
+// Runtime types
+//
+// Each MakeInterface operation is a conversion from a non-interface
+// type to an interface type. The semantics of this operation requires
+// a runtime type descriptor, which is the type portion of an
+// interface, and the value abstracted by reflect.Type.
+//
+// The program accumulates all non-parameterized types that are
+// encountered as MakeInterface operands, along with all types that
+// may be derived from them using reflection. This set is available as
+// Program.RuntimeTypes, and the methods of these types may be
+// reachable via interface calls or reflection even if they are never
+// referenced from the SSA IR. (In practice, algorithms such as RTA
+// that compute reachability from package main perform their own
+// tracking of runtime types at a finer grain, so this feature is not
+// very useful.)
+//
+// Function literals
+//
+// Anonymous functions must be built as soon as they are encountered,
+// as it may affect locals of the enclosing function, but they are not
+// marked 'built' until the end of the outermost enclosing function.
+// (Among other things, this causes them to be logged in top-down order.)
+//
+// The Function.build fields determines the algorithm for building the
+// function body. It is cleared to mark that building is complete.
 
 import (
 	"fmt"
@@ -30,11 +78,14 @@ import (
 	"go/constant"
 	"go/token"
 	"go/types"
-	"go/version"
 	"os"
+	"runtime"
+	"slices"
+	"sync"
 
 	"honnef.co/go/tools/analysis/lint"
 	"honnef.co/go/tools/go/types/typeutil"
+	"honnef.co/go/tools/internal/xtools-internal/versions"
 
 	"golang.org/x/exp/typeparams"
 )
@@ -45,6 +96,8 @@ var (
 
 	// Type constants.
 	tBool       = types.Typ[types.Bool]
+	tByte       = types.Typ[types.Byte]
+	tRune       = types.Universe.Lookup("rune").Type() // prints as "rune" (Typ[Rune] is same as Int32)
 	tInt        = types.Typ[types.Int]
 	tInvalid    = types.Typ[types.Invalid]
 	tString     = types.Typ[types.String]
@@ -52,39 +105,66 @@ var (
 	tEface      = types.NewInterfaceType(nil, nil).Complete()
 	tDeferStack = types.NewPointer(typeutil.NewDeferStack())
 
+	vOne      = intConst(1, nil)
+	vTrue     = NewConst(constant.MakeBool(true), tBool, nil)
+	vNoReturn = NewConst(constant.MakeString("noreturn"), tString, nil)
+
+	jReady        = intConst(0, nil)  // range-over-func jump is READY
+	jBusy         = intConst(-1, nil) // range-over-func jump is BUSY
+	jDone         = intConst(-2, nil) // range-over-func jump is DONE
+	jDroppedPanic = stringConst("iterator call did not preserve panic", nil)
+	jLateYield    = stringConst("yield function called after range loop exit", nil)
+
 	vDeferStack = &Builtin{
 		name: "ssa:deferstack",
 		sig:  types.NewSignatureType(nil, nil, nil, nil, types.NewTuple(anonVar(tDeferStack)), false),
 	}
 )
 
-// range-over-func jump is READY
-func jReady() *Const {
-	c := intConst(0, nil)
-	c.comment = "rangefunc.exit.ready"
-	return c
-}
+// builder holds state associated with the package currently being built.
+// Its methods contain all the logic for AST-to-IR conversion.
+//
+// All Functions belong to the same Program.
+//
+// builders are not thread-safe.
+type builder struct {
+	fns []*Function // Functions that have finished their CREATE phases.
+
+	finished int // finished is the length of the prefix of fns containing built functions.
 
-// range-over-func jump is BUSY
-func jBusy() *Const {
-	c := intConst(-1, nil)
-	c.comment = "rangefunc.exit.busy"
-	return c
+	// The task of building shared functions within the builder.
+	// Shared functions are ones the builder may either create or lookup.
+	// These may be built by other builders in parallel.
+	// The task is done when the builder has finished iterating, and it
+	// waits for all shared functions to finish building.
+	// nil implies there are no hared functions to wait on.
+	buildshared *task
 }
 
-// range-over-func jump is DONE
-func jDone() *Const {
-	c := intConst(-2, nil)
-	c.comment = "rangefunc.exit.done"
-	return c
+// shared is done when the builder has built all of the
+// enqueued functions to a fixed-point.
+func (b *builder) shared() *task {
+	if b.buildshared == nil { // lazily-initialize
+		b.buildshared = &task{done: make(chan unit)}
+	}
+	return b.buildshared
 }
 
-// builder holds state associated with the package currently being built.
-// Its methods contain all the logic for AST-to-IR conversion.
-type builder struct {
-	printFunc string
+// enqueue fn to be built by the builder.
+func (b *builder) enqueue(fn *Function) {
+	b.fns = append(b.fns, fn)
+}
 
-	blocksets [5]BlockSet
+// waitForSharedFunction indicates that the builder should wait until
+// the potentially shared function fn has finished building.
+//
+// This should include any functions that may be built by other
+// builders.
+func (b *builder) waitForSharedFunction(fn *Function) {
+	if fn.buildshared != nil { // maybe need to wait?
+		s := b.shared()
+		s.addEdge(fn.buildshared)
+	}
 }
 
 // cond emits to fn code to evaluate boolean condition e and jump
@@ -136,17 +216,17 @@ func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value {
 	// T(e) = T(e.X) = T(e.Y) after untyped constants have been
 	// eliminated.
 	// TODO(adonovan): not true; MyBool==MyBool yields UntypedBool.
-	t := fn.Pkg.typeOf(e)
+	t := fn.typeOf(e)
 
 	var short Value // value of the short-circuit path
 	switch e.Op {
 	case token.LAND:
 		b.cond(fn, e.X, rhs, done)
-		short = emitConst(fn, NewConst(constant.MakeBool(false), t, e))
+		short = NewConst(constant.MakeBool(false), t, e)
 
 	case token.LOR:
 		b.cond(fn, e.X, done, rhs)
-		short = emitConst(fn, NewConst(constant.MakeBool(true), t, e))
+		short = NewConst(constant.MakeBool(true), t, e)
 	}
 
 	// Is rhs unreachable?
@@ -189,7 +269,7 @@ func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value {
 // assignment or return statement, and "value,ok" uses of
 // TypeAssertExpr, IndexExpr (when X is a map), and Recv.
 func (b *builder) exprN(fn *Function, e ast.Expr) Value {
-	typ := fn.Pkg.typeOf(e).(*types.Tuple)
+	typ := fn.typeOf(e).(*types.Tuple)
 	switch e := e.(type) {
 	case *ast.ParenExpr:
 		return b.exprN(fn, e.X)
@@ -204,7 +284,7 @@ func (b *builder) exprN(fn *Function, e ast.Expr) Value {
 		return emitCall(fn, &c, e)
 
 	case *ast.IndexExpr:
-		mapt := typeutil.CoreType(fn.Pkg.typeOf(e.X)).Underlying().(*types.Map)
+		mapt := typeutil.CoreType(fn.typeOf(e.X)).(*types.Map) // ,ok must be a map.
 		lookup := &MapLookup{
 			X:       b.expr(fn, e.X),
 			Index:   emitConv(fn, b.expr(fn, e.Index), mapt.Key(), e),
@@ -230,17 +310,10 @@ func (b *builder) exprN(fn *Function, e ast.Expr) Value {
 // the caller should treat this like an ordinary library function
 // call.
 func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ types.Type, source ast.Node) Value {
+	typ = fn.typ(typ)
 	switch obj.Name() {
 	case "make":
-		styp := typ.Underlying()
-		if _, ok := typ.Underlying().(*types.Interface); ok {
-			// This must be a type parameter with a core type.
-			// Set styp to the core type and generate instructions based on it.
-			assert(typeparams.IsTypeParam(typ))
-			styp = typeutil.CoreType(typ)
-			assert(styp != nil)
-		}
-		switch styp.(type) {
+		switch ct := typeutil.CoreType(typ).(type) {
 		case *types.Slice:
 			n := b.expr(fn, args[1])
 			m := n
@@ -250,7 +323,7 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ
 			if m, ok := m.(*Const); ok {
 				// treat make([]T, n, m) as new([m]T)[:n]
 				cap := m.Int64()
-				at := types.NewArray(styp.Underlying().(*types.Slice).Elem(), cap)
+				at := types.NewArray(ct.Elem(), cap)
 				v := &Slice{
 					X:    emitNew(fn, at, source, "makeslice"),
 					High: n,
@@ -275,7 +348,7 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ
 			return fn.emit(v, source)
 
 		case *types.Chan:
-			var sz Value = emitConst(fn, intConst(0, source))
+			var sz Value = intConst(0, source)
 			if len(args) == 2 {
 				sz = b.expr(fn, args[1])
 			}
@@ -289,7 +362,8 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ
 
 	case "new":
 		alloc := emitNew(fn, deref(typ), source, "new")
-		if !fn.Pkg.info.Types[args[0]].IsType() {
+		if !fn.info.Types[args[0]].IsType() {
+			// new(expr), requires go1.26
 			v := b.expr(fn, args[0])
 			emitStore(fn, alloc, v, source)
 		}
@@ -305,10 +379,10 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ
 		//
 		// Technically this shouldn't apply to type parameters because their length/capacity is never constant. We still
 		// choose to treat them as constant so that users of the IR get the practically constant length for free.
-		t := typeutil.CoreType(deref(fn.Pkg.typeOf(args[0])))
+		t := typeutil.CoreType(deref(fn.typeOf(args[0])))
 		if at, ok := t.(*types.Array); ok {
 			b.expr(fn, args[0]) // for effects only
-			return emitConst(fn, intConst(at.Len(), args[0]))
+			return intConst(at.Len(), args[0])
 		}
 		// Otherwise treat as normal.
 
@@ -316,9 +390,8 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ
 		fn.emit(&Panic{
 			X: emitConv(fn, b.expr(fn, args[0]), tEface, source),
 		}, source)
-		addEdge(fn.currentBlock, fn.Exit)
 		fn.currentBlock = fn.newBasicBlock("unreachable")
-		return emitConst(fn, NewConst(constant.MakeBool(true), tBool, nil)) // any non-nil Value will do
+		return vTrue // any non-nil Value will do
 	}
 	return nil // treat all others as a regular function call
 }
@@ -345,21 +418,23 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ
 // - &x, including when implicit in method call or composite literals.
 // - a[:] iff a is an array (not *array)
 // - references to variables in lexically enclosing functions.
-func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) (RET lvalue) {
+func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
 	switch e := e.(type) {
 	case *ast.Ident:
 		if isBlankIdent(e) {
 			return blank{}
 		}
-		obj := fn.Pkg.objectOf(e)
-		v := fn.Prog.packageLevelValue(obj) // var (address)
-		if v == nil {
-			v = fn.lookup(obj.(*types.Var), escaping)
+		obj := fn.objectOf(e).(*types.Var)
+		var v Value
+		if g := fn.Prog.packageLevelMember(obj); g != nil {
+			v = g.(*Global) // var (address)
+		} else {
+			v = fn.lookup(obj, escaping)
 		}
 		return &address{addr: v, expr: e}
 
 	case *ast.CompositeLit:
-		t := deref(fn.Pkg.typeOf(e))
+		t := deref(fn.typeOf(e))
 		var v *Alloc
 		if escaping {
 			v = emitNew(fn, t, e, "complit")
@@ -375,19 +450,19 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) (RET lvalue) {
 		return b.addr(fn, e.X, escaping)
 
 	case *ast.SelectorExpr:
-		sel, ok := fn.Pkg.info.Selections[e]
-		if !ok {
+		sel := fn.selection(e)
+		if sel == nil {
 			// qualified identifier
 			return b.addr(fn, e.Sel, escaping)
 		}
-		if sel.Kind() != types.FieldVal {
+		if sel.kind != types.FieldVal {
 			panic(sel)
 		}
 		wantAddr := true
 		v := b.receiver(fn, e.X, wantAddr, escaping, sel, e)
-		index := sel.Index()[len(sel.Index())-1]
-		vut := typeutil.CoreType(deref(v.Type())).Underlying().(*types.Struct)
-		fld := vut.Field(index)
+		index := sel.index[len(sel.index)-1]
+		fld := fieldOf(deref(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
 		// evaluating x and p().
@@ -397,61 +472,35 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) (RET lvalue) {
 		return &lazyAddress{addr: emit, t: fld.Type(), expr: e.Sel}
 
 	case *ast.IndexExpr:
+		xt := fn.typeOf(e.X)
+		elem, mode := indexType(xt)
 		var x Value
 		var et types.Type
-		xt := fn.Pkg.typeOf(e.X)
-
-		// Indexing doesn't need a core type, it only requires all types to be similar enough. For example, []int64 |
-		// [5]int64 can be indexed. The element types do have to match though.
-
-		terms, err := typeparams.NormalTerms(xt)
-		if err != nil {
-			panic(fmt.Sprintf("unexpected error: %s", err))
-		}
-		isArrayLike := func() (types.Type, bool) {
-			for _, term := range terms {
-				arr, ok := term.Type().Underlying().(*types.Array)
-				if ok {
-					return arr.Elem(), true
-				}
-			}
-			return nil, false
-		}
-
-		isSliceLike := func() (types.Type, bool) {
-			for _, term := range terms {
-				switch t := term.Type().Underlying().(type) {
-				case *types.Slice:
-					return t.Elem(), true
-				case *types.Pointer:
-					return t.Elem().Underlying().(*types.Array).Elem(), true
-				}
-			}
-			return nil, false
-		}
-
-		if elem, ok := isArrayLike(); ok {
-			// array
+		switch mode {
+		case ixArrVar: // array, array|slice, array|*array, or array|*array|slice.
 			x = b.addr(fn, e.X, escaping).address(fn)
 			et = types.NewPointer(elem)
-		} else if elem, ok := isSliceLike(); ok {
-			// slice or *array
+		case ixVar: // *array, slice, *array|slice
 			x = b.expr(fn, e.X)
 			et = types.NewPointer(elem)
-		} else if t, ok := typeutil.CoreType(xt).Underlying().(*types.Map); ok {
+		case ixMap:
+			mt := typeutil.CoreType(xt).(*types.Map)
 			return &element{
 				m: b.expr(fn, e.X),
-				k: emitConv(fn, b.expr(fn, e.Index), t.Key(), e.Index),
-				t: t.Elem(),
+				k: emitConv(fn, b.expr(fn, e.Index), mt.Key(), e.Index),
+				t: mt.Elem(),
 			}
-		} else {
-			panic("unexpected container type in IndexExpr: " + t.String())
+		default:
+			panic("unexpected container type in IndexExpr: " + xt.String())
+		}
+		index := b.expr(fn, e.Index)
+		if isUntyped(index.Type()) {
+			index = emitConv(fn, index, tInt, e.Index)
 		}
 
 		// Due to the two phases of resolving AssignStmt, a panic from x[i] = p()
 		// when x is nil or i is out-of-bounds is required to come after the
 		// side-effects of evaluating x, i and p().
-		index := b.expr(fn, e.Index)
 		emit := func(fn *Function) Value {
 			v := &IndexAddr{
 				X:     x,
@@ -475,7 +524,7 @@ type store struct {
 	source ast.Node
 
 	// if debugRef is set no other fields will be set
-	debugRef *DebugRef
+	debugRef *debugRef
 }
 
 type storebuf struct{ stores []store }
@@ -484,7 +533,7 @@ func (sb *storebuf) store(lhs lvalue, rhs Value, source ast.Node) {
 	sb.stores = append(sb.stores, store{lhs, rhs, source, nil})
 }
 
-func (sb *storebuf) storeDebugRef(ref *DebugRef) {
+func (sb *storebuf) storeDebugRef(ref *debugRef) {
 	sb.stores = append(sb.stores, store{debugRef: ref})
 }
 
@@ -513,12 +562,12 @@ func (sb *storebuf) emit(fn *Function) {
 // literal that may reference parts of the LHS.
 func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *storebuf, source ast.Node) {
 	// Can we initialize it in place?
-	if e, ok := unparen(e).(*ast.CompositeLit); ok {
+	if e, ok := ast.Unparen(e).(*ast.CompositeLit); ok {
 		// A CompositeLit never evaluates to a pointer,
 		// 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 isPointerCore(loc.typ()) {
 				// Example input that hits this code:
 				//
 				// 	type S1 struct{ X int }
@@ -555,7 +604,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 typeutil.CoreType(loc.typ()).Underlying().(type) {
+				switch typeutil.CoreType(loc.typ()).(type) {
 				case *types.Struct, *types.Array:
 					if sb != nil {
 						// Make sure we don't emit DebugRefs before the store has actually occurred
@@ -584,13 +633,13 @@ func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *
 // expr lowers a single-result expression e to IR form, emitting code
 // to fn and returning the Value defined by the expression.
 func (b *builder) expr(fn *Function, e ast.Expr) Value {
-	e = unparen(e)
+	e = ast.Unparen(e)
 
-	tv := fn.Pkg.info.Types[e]
+	tv := fn.info.Types[e]
 
 	// Is expression a constant?
 	if tv.Value != nil {
-		return emitConst(fn, NewConst(tv.Value, tv.Type, e))
+		return NewConst(tv.Value, fn.typ(tv.Type), e)
 	}
 
 	var v Value
@@ -614,26 +663,35 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
 		panic("non-constant BasicLit") // unreachable
 
 	case *ast.FuncLit:
+		/* function literal */
 		fn2 := &Function{
-			name:         fmt.Sprintf("%s$%d", fn.Name(), 1+len(fn.AnonFuncs)),
-			Signature:    fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature),
-			parent:       fn,
-			Pkg:          fn.Pkg,
-			Prog:         fn.Prog,
-			functionBody: new(functionBody),
-			goversion:    fn.goversion, // share the parent's goversion
+			name:           fmt.Sprintf("%s$%d", fn.Name(), 1+len(fn.AnonFuncs)),
+			Signature:      fn.typeOf(e.Type).(*types.Signature),
+			pos:            e.Type.Func,
+			parent:         fn,
+			anonIdx:        int32(len(fn.AnonFuncs)),
+			Pkg:            fn.Pkg,
+			Prog:           fn.Prog,
+			syntax:         e,
+			info:           fn.info,
+			goversion:      fn.goversion,
+			build:          (*builder).buildFromSyntax,
+			topLevelOrigin: nil,           // use anonIdx to lookup an anon instance's origin.
+			typeparams:     fn.typeparams, // share the parent's type parameters.
+			typeargs:       fn.typeargs,   // share the parent's type arguments.
+			subst:          fn.subst,      // share the parent's type substitutions.
 		}
 		fn2.uniq = fn.uniq // start from parent's unique values
-		fn2.source = e
 		fn.AnonFuncs = append(fn.AnonFuncs, fn2)
-		fn2.initHTML(b.printFunc)
-		b.buildFunction(fn2)
+		// Build anon immediately, as it may cause fn's locals to escape.
+		// (It is not marked 'built' until the end of the enclosing FuncDecl.)
+		fn2.build(b, fn2)
 		fn.uniq = fn2.uniq // resume after anon's unique values
 		if fn2.FreeVars == nil {
 			return fn2
 		}
 		v := &MakeClosure{Fn: fn2}
-		v.setType(tv.Type)
+		v.setType(fn.typ(tv.Type))
 		for _, fv := range fn2.FreeVars {
 			v.Bindings = append(v.Bindings, fv.outer)
 			fv.outer = nil
@@ -641,19 +699,19 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
 		return fn.emit(v, e)
 
 	case *ast.TypeAssertExpr: // single-result form only
-		return emitTypeAssert(fn, b.expr(fn, e.X), tv.Type, e)
+		return emitTypeAssert(fn, b.expr(fn, e.X), fn.typ(tv.Type), e)
 
 	case *ast.CallExpr:
-		if fn.Pkg.info.Types[e.Fun].IsType() {
+		if fn.info.Types[e.Fun].IsType() {
 			// Explicit type conversion, e.g. string(x) or big.Int(x)
 			x := b.expr(fn, e.Args[0])
-			y := emitConv(fn, x, tv.Type, e)
+			y := emitConv(fn, x, fn.typ(tv.Type), e)
 			return y
 		}
 		// Call to "intrinsic" built-ins, e.g. new, make, panic.
-		if id, ok := unparen(e.Fun).(*ast.Ident); ok {
-			if obj, ok := fn.Pkg.info.Uses[id].(*types.Builtin); ok {
-				if v := b.builtin(fn, obj, e.Args, tv.Type, e); v != nil {
+		if id, ok := ast.Unparen(e.Fun).(*ast.Ident); ok {
+			if obj, ok := fn.info.Uses[id].(*types.Builtin); ok {
+				if v := b.builtin(fn, obj, e.Args, fn.typ(tv.Type), e); v != nil {
 					return v
 				}
 			}
@@ -661,14 +719,14 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
 		// Regular function call.
 		var v Call
 		b.setCall(fn, e, &v.Call)
-		v.setType(tv.Type)
+		v.setType(fn.typ(tv.Type))
 		return emitCall(fn, &v, e)
 
 	case *ast.UnaryExpr:
 		switch e.Op {
 		case token.AND: // &X --- potentially escaping.
 			addr := b.addr(fn, e.X, true)
-			if _, ok := unparen(e.X).(*ast.StarExpr); ok {
+			if _, ok := ast.Unparen(e.X).(*ast.StarExpr); ok {
 				// &*p must panic if p is nil (https://golang.org/s/go12nil).
 				// For simplicity, we'll just (suboptimally) rely
 				// on the side effects of a load.
@@ -683,10 +741,10 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
 				Op: e.Op,
 				X:  b.expr(fn, e.X),
 			}
-			v.setType(tv.Type)
+			v.setType(fn.typ(tv.Type))
 			return fn.emit(v, e)
 		case token.ARROW:
-			return emitRecv(fn, b.expr(fn, e.X), false, tv.Type, e)
+			return emitRecv(fn, b.expr(fn, e.X), false, fn.typ(tv.Type), e)
 		default:
 			panic(e.Op)
 		}
@@ -698,35 +756,34 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
 		case token.SHL, token.SHR:
 			fallthrough
 		case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
-			return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), tv.Type, e)
+			return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), fn.typ(tv.Type), e)
 
 		case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ:
 			cmp := emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e)
 			// The type of x==y may be UntypedBool.
-			return emitConv(fn, cmp, types.Default(tv.Type), e)
+			return emitConv(fn, cmp, types.Default(fn.typ(tv.Type)), e)
 		default:
 			panic("illegal op in BinaryExpr: " + e.Op.String())
 		}
 
 	case *ast.SliceExpr:
+		var low, high, max Value
 		var x Value
-		if core := typeutil.CoreType(fn.Pkg.typeOf(e.X)); core != nil {
-			switch core.Underlying().(type) {
-			case *types.Array:
-				// Potentially escaping.
-				x = b.addr(fn, e.X, true).address(fn)
-			case *types.Basic, *types.Slice, *types.Pointer: // *array
-				x = b.expr(fn, e.X)
-			default:
-				panic("unreachable")
-			}
-		} else {
-			// We're indexing a string | []byte. Note that other combinations such as []byte | [4]byte are currently not
-			// allowed by the language.
+		xtyp := fn.typeOf(e.X)
+		switch typeutil.CoreType(xtyp).(type) {
+		case *types.Array:
+			// Potentially escaping.
+			x = b.addr(fn, e.X, true).address(fn)
+		case *types.Basic, *types.Slice, *types.Pointer: // *array
 			x = b.expr(fn, e.X)
+		default:
+			// core type exception?
+			if isBytestring(xtyp) {
+				x = b.expr(fn, e.X) // bytestring is handled as string and []byte.
+			} else {
+				panic("unexpected sequence type in SliceExpr")
+			}
 		}
-
-		var low, high, max Value
 		if e.Low != nil {
 			low = b.expr(fn, e.Low)
 		}
@@ -742,72 +799,112 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
 			High: high,
 			Max:  max,
 		}
-		v.setType(tv.Type)
+		v.setType(fn.typ(tv.Type))
 		return fn.emit(v, e)
 
 	case *ast.Ident:
-		obj := fn.Pkg.info.Uses[e]
+		obj := fn.info.Uses[e]
 		// Universal built-in or nil?
 		switch obj := obj.(type) {
 		case *types.Builtin:
-			return &Builtin{name: obj.Name(), sig: tv.Type.(*types.Signature)}
+			return &Builtin{name: obj.Name(), sig: fn.instanceType(e).(*types.Signature)}
 		case *types.Nil:
-			return emitConst(fn, nilConst(tv.Type, e))
+			return zeroConst(fn.instanceType(e), e)
 		}
+
 		// Package-level func or var?
-		if v := fn.Prog.packageLevelValue(obj); v != nil {
-			if _, ok := obj.(*types.Var); ok {
-				return emitLoad(fn, v, e) // var (address)
+		// (obj must belong to same package or a direct import.)
+		if v := fn.Prog.packageLevelMember(obj); v != nil {
+			if g, ok := v.(*Global); ok {
+				return emitLoad(fn, g, e) // var (address)
 			}
-			if instance, ok := fn.Pkg.info.Instances[e]; ok {
-				// Instantiated generic function
-				return makeInstance(fn.Prog, v.(*Function), instance.Type.(*types.Signature), instance.TypeArgs)
+			callee := v.(*Function) // (func)
+			if callee.typeparams.Len() > 0 {
+				targs := fn.subtargs(e)
+				callee = callee.instance(nil, targs, b)
 			}
-			return v // (func)
+			return callee
 		}
 		// Local var.
 		return emitLoad(fn, fn.lookup(obj.(*types.Var), false), e) // var (address)
 
 	case *ast.SelectorExpr:
-		sel, ok := fn.Pkg.info.Selections[e]
-		if !ok {
+		sel := fn.selection(e)
+		if sel == nil {
 			// builtin unsafe.{Add,Slice}
-			if obj, ok := fn.Pkg.info.Uses[e.Sel].(*types.Builtin); ok {
-				return &Builtin{name: "Unsafe" + obj.Name(), sig: tv.Type.(*types.Signature)}
+			if obj, ok := fn.info.Uses[e.Sel].(*types.Builtin); ok {
+				return &Builtin{name: "Unsafe" + obj.Name(), sig: fn.typ(tv.Type).(*types.Signature)}
 			}
 			// qualified identifier
 			return b.expr(fn, e.Sel)
 		}
-		switch sel.Kind() {
+		switch sel.kind {
 		case types.MethodExpr:
 			// (*T).f or T.f, the method f from the method-set of type T.
 			// The result is a "thunk".
-			return emitConv(fn, makeThunk(fn.Prog, sel), tv.Type, e)
+			targs := fn.subtargs(e.Sel)
+			thunk := createThunk(fn.Prog, sel, targs)
+			b.enqueue(thunk)
+			return thunk
 
 		case types.MethodVal:
 			// e.f where e is an expression and f is a method.
 			// The result is a "bound".
-			obj := sel.Obj().(*types.Func)
-			rt := recvType(obj)
+			m := sel.obj.(*types.Func)
+			rt := fn.typ(recvType(m))
 			wantAddr := isPointer(rt)
 			escaping := true
 			v := b.receiver(fn, e.X, wantAddr, escaping, sel, e)
+
 			if types.IsInterface(rt) {
-				// If v has interface type I,
+				// If v may be an interface type I (after instantiating),
 				// we must emit a check that v is non-nil.
-				// We use: typeassert v.(I).
-				emitTypeAssert(fn, v, rt, e)
+				if recv, ok := types.Unalias(sel.recv).(*types.TypeParam); ok {
+					// Emit a nil check if any possible instantiation of the
+					// type parameter is an interface type.
+					if !typeSetIsEmpty(recv) {
+						// recv has a concrete term its typeset.
+						// So it cannot be instantiated as an interface.
+						//
+						// Example:
+						// func _[T interface{~int; Foo()}] () {
+						//    var v T
+						//    _ = v.Foo // <-- MethodVal
+						// }
+					} else {
+						// rt may be instantiated as an interface.
+						// Emit nil check: typeassert (any(v)).(any).
+						emitTypeAssert(fn, emitConv(fn, v, tEface, nil), tEface, nil)
+					}
+				} else {
+					// non-type param interface
+					// Emit nil check: typeassert v.(I).
+					emitTypeAssert(fn, v, rt, e.Sel)
+				}
+			}
+
+			if rtargs := fn.subrtargs(m); len(rtargs) > 0 {
+				m = fn.Prog.canon.instantiateMethod(m, rtargs, fn.Prog.ctxt)
 			}
+
+			targs := fn.subtargs(e.Sel)
+			bound := createBound(fn.Prog, m, targs)
+			b.enqueue(bound)
+
+			// The assignment may widen a type parameter to its
+			// interface bound (case #3 of go.dev/issue.78110).
+			v = emitConv(fn, v, bound.FreeVars[0].Type(), nil)
+
 			c := &MakeClosure{
-				Fn:       makeBound(fn.Prog, obj),
+				Fn:       bound,
 				Bindings: []Value{v},
 			}
 			c.source = e.Sel
-			c.setType(tv.Type)
-			return fn.emit(c, e)
+			c.setType(bound.Signature)
+			return fn.emit(c, e.Sel)
 
 		case types.FieldVal:
-			indices := sel.Index()
+			indices := sel.index
 			last := len(indices) - 1
 			v := b.expr(fn, e.X)
 			v = emitImplicitSelections(fn, v, indices[:last], e)
@@ -817,69 +914,52 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
 
 		panic("unexpected expression-relative selector")
 
-	case *ast.IndexExpr:
-		// IndexExpr might either be an actual indexing operation, or an instantiation
-		xt := fn.Pkg.typeOf(e.X)
-
-		terms, err := typeparams.NormalTerms(xt)
-		if err != nil {
-			panic(fmt.Sprintf("unexpected error: %s", err))
-		}
-		isNonAddressableIndexable := func() (types.Type, bool) {
-			for _, term := range terms {
-				switch t := term.Type().Underlying().(type) {
-				case *types.Array:
-					return t.Elem(), true
-				case *types.Basic:
-					// a string
-					return types.Universe.Lookup("byte").Type(), true
-				}
-			}
-			return nil, false
+	case *ast.IndexListExpr:
+		// f[X, Y] must be a generic function
+		if !instance(fn.info, e.X) {
+			panic("unexpected expression-could not match index list to instantiation")
 		}
+		return b.expr(fn, e.X) // Handle instantiation within the *Ident or *SelectorExpr cases.
 
-		isAddressableIndexable := func() (types.Type, bool) {
-			for _, term := range terms {
-				switch t := term.Type().Underlying().(type) {
-				case *types.Slice:
-					return t.Elem(), true
-				case *types.Pointer:
-					return t.Elem().Underlying().(*types.Array).Elem(), true
-				}
-			}
-			return nil, false
-		}
+	case *ast.IndexExpr:
+		if instance(fn.info, e.X) {
+			return b.expr(fn, e.X) // Handle instantiation within the *Ident or *SelectorExpr cases.
+		}
+		// not a generic instantiation.
+		xt := fn.typeOf(e.X)
+		switch et, mode := indexType(xt); mode {
+		case ixVar:
+			// Addressable slice/array; use IndexAddr and Load.
+			return b.addr(fn, e, false).load(fn, e)
+
+		case ixArrVar, ixValue:
+			// An array in a register, a string or a combined type that contains
+			// either an [_]array (ixArrVar) or string (ixValue).
 
-		if elem, ok := isNonAddressableIndexable(); ok {
-			// At least one of the types is non-addressable
+			// Note: for ixArrVar and CoreType(xt)==nil can be IndexAddr and Load.
+			index := b.expr(fn, e.Index)
+			if isUntyped(index.Type()) {
+				index = emitConv(fn, index, tInt, e.Index)
+			}
 			v := &Index{
 				X:     b.expr(fn, e.X),
-				Index: b.expr(fn, e.Index),
+				Index: index,
 			}
-			v.setType(elem)
+			v.setType(et)
 			return fn.emit(v, e)
-		} else if _, ok := isAddressableIndexable(); ok {
-			// All types are addressable (otherwise the previous branch would've fired)
-			return b.addr(fn, e, false).load(fn, e)
-		} else if t, ok := typeutil.CoreType(xt).Underlying().(*types.Map); ok {
-			// Maps are not addressable.
+
+		case ixMap:
+			ct := typeutil.CoreType(xt).(*types.Map)
 			v := &MapLookup{
 				X:     b.expr(fn, e.X),
-				Index: emitConv(fn, b.expr(fn, e.Index), t.Key(), e.Index),
+				Index: emitConv(fn, b.expr(fn, e.Index), ct.Key(), e.Index),
 			}
-			v.setType(t.Elem())
+			v.setType(ct.Elem())
 			return fn.emit(v, e)
-		} else if _, ok := xt.Underlying().(*types.Signature); ok {
-			// Instantiating a generic function
-			return b.expr(fn, e.X)
-		} else {
-			panic("unexpected container type in IndexExpr: " + t.String())
+		default:
+			panic("unexpected container type in IndexExpr: " + xt.String())
 		}
 
-	case *ast.IndexListExpr:
-		// Instantiating a generic function
-		return b.expr(fn, e.X)
-
 	case *ast.CompositeLit, *ast.StarExpr:
 		// Addressable types (lvalues)
 		return b.addr(fn, e, false).load(fn, e)
@@ -901,21 +981,24 @@ func (b *builder) stmtList(fn *Function, list []ast.Stmt) {
 // selections of sel.
 //
 // wantAddr requests that the result is an address.  If
-// !sel.Indirect(), this may require that e be built in addr() mode; it
+// !sel.indirect, this may require that e be built in addr() mode; it
 // must thus be addressable.
 //
 // escaping is defined as per builder.addr().
-func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *types.Selection, source ast.Node) Value {
+func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *selection, source ast.Node) Value {
 	var v Value
-	if wantAddr && !sel.Indirect() && !isPointer(fn.Pkg.typeOf(e)) {
+	if wantAddr && !sel.indirect && !isPointerCore(fn.typeOf(e)) {
 		v = b.addr(fn, e, escaping).address(fn)
 	} else {
 		v = b.expr(fn, e)
 	}
 
-	last := len(sel.Index()) - 1
-	v = emitImplicitSelections(fn, v, sel.Index()[:last], source)
-	if !wantAddr && isPointer(v.Type()) {
+	last := len(sel.index) - 1
+	v = emitImplicitSelections(fn, v, sel.index[:last], source)
+	if types.IsInterface(v.Type()) {
+		// When v is an interface, sel.Kind()==MethodValue and v.f is invoked.
+		// So v is not loaded, even if v has a pointer core type.
+	} else if !wantAddr && isPointerCore(v.Type()) {
 		v = emitLoad(fn, v, e)
 	}
 	return v
@@ -925,33 +1008,37 @@ func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, se
 // (Func, Method, Recv, Args[0]) based on the kind of invocation
 // occurring in e.
 func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
-	// Is this a method call?
-	if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok {
-		sel, ok := fn.Pkg.info.Selections[selector]
-		if ok && sel.Kind() == types.MethodVal {
-			obj := sel.Obj().(*types.Func)
+	// Is this a (possibly generic) method call?
+	m := ast.Unparen(e.Fun)
+	switch e := m.(type) {
+	case *ast.IndexExpr:
+		m = e.X
+	case *ast.IndexListExpr:
+		m = e.X
+	}
+	if selector, ok := m.(*ast.SelectorExpr); ok {
+		sel := fn.selection(selector)
+		if sel != nil && sel.kind == types.MethodVal {
+			obj := sel.obj.(*types.Func)
 			recv := recvType(obj)
+
 			wantAddr := isPointer(recv)
 			escaping := true
 			v := b.receiver(fn, selector.X, wantAddr, escaping, sel, selector)
 			if types.IsInterface(recv) {
 				// Invoke-mode call.
-
-				// Methods in interfaces cannot have their own type parameters, so we needn't do anything for type
-				// parameters.
-				c.Value = v
+				c.Value = v // possibly type param
 				c.Method = obj
 			} else {
 				// "Call"-mode call.
-
-				// declaredFunc takes care of creating wrappers for functions with type parameters.
-				c.Value = fn.Prog.declaredFunc(obj)
+				targs := fn.subtargs(selector.Sel)
+				c.Value = fn.Prog.objectMethod(obj, targs, b)
 				c.Args = append(c.Args, v)
 			}
 			return
 		}
 
-		// sel.Kind()==MethodExpr indicates T.f() or (*T).f():
+		// sel.kind==MethodExpr indicates T.f() or (*T).f():
 		// a statically dispatched call to the method f in the
 		// method-set of T or *T.  T may be an interface.
 		//
@@ -981,9 +1068,8 @@ func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
 		// - apply implicit field selections.
 		// - use MethodVal logic to populate fields of c.
 	}
+
 	// Evaluate the function operand in the usual way.
-	//
-	// Code in expr takes care of creating wrappers for functions with type parameters.
 	c.Value = b.expr(fn, e.Fun)
 }
 
@@ -1034,16 +1120,15 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx
 		st := sig.Params().At(np).Type().(*types.Slice)
 		vt := st.Elem()
 		if len(varargs) == 0 {
-			args = append(args, emitConst(fn, nilConst(st, nil)))
+			args = append(args, zeroConst(st, nil))
 		} else {
 			// Replace a suffix of args with a slice containing it.
 			at := types.NewArray(vt, int64(len(varargs)))
 			a := emitNew(fn, at, e, "varargs")
-			a.source = e
 			for i, arg := range varargs {
 				iaddr := &IndexAddr{
 					X:     a,
-					Index: emitConst(fn, intConst(int64(i), nil)),
+					Index: intConst(int64(i), nil),
 				}
 				iaddr.setType(types.NewPointer(vt))
 				fn.emit(iaddr, e)
@@ -1065,7 +1150,7 @@ func (b *builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) {
 	b.setCallFunc(fn, e, c)
 
 	// Then append the other actual parameters.
-	sig, _ := typeutil.CoreType(fn.Pkg.typeOf(e.Fun)).(*types.Signature)
+	sig, _ := typeutil.CoreType(fn.typeOf(e.Fun)).(*types.Signature)
 	if sig == nil {
 		panic(fmt.Sprintf("no signature for call of %s", e.Fun))
 	}
@@ -1129,7 +1214,7 @@ func (b *builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool, so
 		var lval lvalue = blank{}
 		if !isBlankIdent(lhs) {
 			if isDef {
-				if obj, ok := fn.Pkg.info.Defs[lhs.(*ast.Ident)].(*types.Var); ok {
+				if obj, ok := fn.info.Defs[lhs.(*ast.Ident)].(*types.Var); ok {
 					emitLocalVar(fn, obj, lhs)
 					isZero[i] = true
 				}
@@ -1190,56 +1275,121 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
 //	x := T{a: 1}
 //	x = T{a: x.a}
 //
-// all the reads must occur before all the writes. This is implicitly handled by the write buffering effected by
-// compositeElement and explicitly by the storebuf for when we don't use CompositeValue.
+// all the reads must occur before all the writes. Thus all stores to
+// loc are emitted to the storebuf sb for later execution.
 //
 // A CompositeLit may have pointer type only in the recursive (nested)
 // case when the type name is implicit.  e.g. in []*T{{}}, the inner
 // 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.Pkg.typeOf(e))
+	typ := deref(fn.typeOf(e)) // retain the named/alias/param type, if any
 	switch t := typeutil.CoreType(typ).(type) {
 	case *types.Struct:
 		lvalue := &address{addr: addr, expr: e}
 		if len(e.Elts) == 0 {
 			if !isZero {
-				sb.store(lvalue, zeroValue(fn, deref(addr.Type()), e), e)
+				sb.store(lvalue, zeroConst(deref(addr.Type()), e), e)
 			}
 		} else {
 			v := &CompositeValue{
 				Values: make([]Value, t.NumFields()),
 			}
-			for i := 0; i < t.NumFields(); i++ {
-				v.Values[i] = emitConst(fn, zeroConst(t.Field(i).Type(), e))
+			for i := range t.NumFields() {
+				v.Values[i] = zeroConst(t.Field(i).Type(), e)
 			}
 			v.setType(typ)
 
+			type chainElement struct {
+				children []chainElement
+				cv       *CompositeValue
+			}
+
+			chain := chainElement{
+				cv: v,
+			}
+
 			for i, e := range e.Elts {
-				fieldIndex := i
 				if kv, ok := e.(*ast.KeyValueExpr); ok {
 					fname := kv.Key.(*ast.Ident).Name
-					for i, n := 0, t.NumFields(); i < n; i++ {
-						sf := t.Field(i)
-						if sf.Name() == fname {
-							fieldIndex = i
-							e = kv.Value
-							break
+					_, index, _ := types.LookupFieldOrMethod(t, true, fn.declaredPackage().Pkg, fname)
+
+					var parent *chainElement
+					chain := &chain
+					chainCoreType := t
+
+					// Ensure that the entire chain of embedded fields has
+					// corresponding CompositeValues
+					for _, idx := range index[:len(index)-1] {
+						if idx >= len(chain.children) {
+							n := make([]chainElement, idx+1)
+							copy(n, chain.children)
+							chain.children = n
+						}
+
+						parent = chain
+						chain = &chain.children[idx]
+						treeType := chainCoreType.Field(idx).Type()
+						chainCoreType = typeutil.CoreType(treeType).(*types.Struct)
+						if chain.cv == nil {
+							ncv := &CompositeValue{
+								Values: make([]Value, chainCoreType.NumFields()),
+							}
+							for i := range chainCoreType.NumFields() {
+								ncv.Values[i] = zeroConst(chainCoreType.Field(i).Type(), kv)
+							}
+							ncv.setType(treeType)
+							chain.cv = ncv
+							ce := &compositeElement{
+								cv:  parent.cv,
+								idx: idx,
+								t:   ncv.Type(),
+							}
+							parent.cv.Bitmap.SetBit(&parent.cv.Bitmap, idx, 1)
+							parent.cv.NumSet++
+							sb.store(ce, chain.cv, kv)
 						}
 					}
+
+					ce := &compositeElement{
+						cv:   chain.cv,
+						idx:  index[len(index)-1],
+						t:    chainCoreType.Field(index[len(index)-1]).Type(),
+						expr: kv.Value,
+					}
+					// We use b.assign for its handling of implicit & (which,
+					// albeit not needed for structs now, may be needed in the
+					// future), but no store buffer because 1) it's not needed 2)
+					// implicit conversions have to be emitted before we emit the
+					// CompositeValue.
+					b.assign(fn, ce, kv.Value, isZero, nil, kv)
+					chain.cv.Bitmap.SetBit(&chain.cv.Bitmap, index[len(index)-1], 1)
+					chain.cv.NumSet++
+				} else {
+					ce := &compositeElement{
+						cv:   v,
+						idx:  i,
+						t:    t.Field(i).Type(),
+						expr: e,
+					}
+					b.assign(fn, ce, e, isZero, nil, e)
+					v.Bitmap.SetBit(&v.Bitmap, i, 1)
+					v.NumSet++
+				}
+			}
+
+			var dfs func(t chainElement)
+			dfs = func(t chainElement) {
+				for _, tt := range t.children {
+					dfs(tt)
 				}
 
-				ce := &compositeElement{
-					cv:   v,
-					idx:  fieldIndex,
-					t:    t.Field(fieldIndex).Type(),
-					expr: e,
+				// XXX better e
+				if t.cv != nil {
+					fn.emit(t.cv, e)
 				}
-				b.assign(fn, ce, e, isZero, sb, e)
-				v.Bitmap.SetBit(&v.Bitmap, fieldIndex, 1)
-				v.NumSet++
 			}
-			fn.emit(v, e)
+			dfs(chain)
 			sb.store(lvalue, v, e)
 		}
 
@@ -1258,7 +1408,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
 		var final Value
 		if len(e.Elts) == 0 {
 			if !isZero {
-				zc := emitConst(fn, zeroConst(at, e))
+				zc := zeroConst(at, e)
 				final = zc
 			}
 		} else {
@@ -1267,7 +1417,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
 				v := &CompositeValue{
 					Values: make([]Value, at.Len()),
 				}
-				zc := emitConst(fn, zeroConst(at.Elem(), e))
+				zc := zeroConst(at.Elem(), e)
 				for i := range v.Values {
 					v.Values[i] = zc
 				}
@@ -1283,7 +1433,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
 						if idx != nil {
 							idxval = idx.Int64() + 1
 						}
-						idx = emitConst(fn, intConst(idxval, e)).(*Const)
+						idx = intConst(idxval, e)
 					}
 
 					iaddr := &compositeElement{
@@ -1293,7 +1443,11 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
 						expr: e,
 					}
 
-					b.assign(fn, iaddr, e, true, sb, e)
+					// We use b.assign for its handling of implicit &, but no
+					// store buffer because 1) it's not needed 2) implicit
+					// conversions have to be emitted before we emit the
+					// CompositeValue.
+					b.assign(fn, iaddr, e, true, nil, e)
 					v.Bitmap.SetBit(&v.Bitmap, int(idx.Int64()), 1)
 					v.NumSet++
 				}
@@ -1304,7 +1458,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
 				// like []int{1<<62: 1}.
 				if !isZero {
 					// memclear
-					sb.store(&address{array, nil}, zeroValue(fn, deref(array.Type()), e), e)
+					sb.store(&address{array, nil}, zeroConst(deref(array.Type()), e), e)
 				}
 
 				var idx *Const
@@ -1317,7 +1471,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
 						if idx != nil {
 							idxval = idx.Int64() + 1
 						}
-						idx = emitConst(fn, intConst(idxval, e)).(*Const)
+						idx = intConst(idxval, e)
 					}
 					iaddr := &IndexAddr{
 						X:     array,
@@ -1346,7 +1500,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
 		}
 
 	case *types.Map:
-		m := &MakeMap{Reserve: emitConst(fn, intConst(int64(len(e.Elts)), e))}
+		m := &MakeMap{Reserve: intConst(int64(len(e.Elts)), e)}
 		m.setType(typ)
 		fn.emit(m, e)
 		for _, e := range e.Elts {
@@ -1358,8 +1512,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 := ast.Unparen(e.Key).(*ast.CompositeLit); ok {
+				wantAddr = isPointerCore(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.
@@ -1385,7 +1544,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
 		sb.store(&address{addr: addr, expr: e}, m, e)
 
 	default:
-		panic("unexpected CompositeLit type: " + t.String())
+		panic("unexpected CompositeLit type: " + typ.String())
 	}
 }
 
@@ -1398,7 +1557,7 @@ func (b *builder) switchStmt(fn *Function, s *ast.SwitchStmt, label *lblock) {
 	for _, iclause := range s.Body.List {
 		clause := iclause.(*ast.CaseClause)
 		for _, cond := range clause.List {
-			if fn.Pkg.info.Types[unparen(cond)].Value == nil {
+			if fn.info.Types[ast.Unparen(cond)].Value == nil {
 				dynamic = true
 				break
 			}
@@ -1501,20 +1660,11 @@ func (b *builder) switchStmtDynamic(fn *Function, s *ast.SwitchStmt, label *lblo
 	if s.Init != nil {
 		b.stmt(fn, s.Init)
 	}
-	kTrue := emitConst(fn, NewConst(constant.MakeBool(true), tBool, nil))
+	var tag Value = vTrue
 
-	var tagv Value = kTrue
-	var tagSource ast.Node = s
 	if s.Tag != nil {
-		tagv = b.expr(fn, s.Tag)
-		tagSource = s.Tag
+		tag = b.expr(fn, s.Tag)
 	}
-	// lifting only considers loads and stores, but we want different
-	// sigma nodes for the different comparisons. use a temporary and
-	// load it in every branch.
-	tag := emitLocal(fn, tagv.Type(), tagSource, "switch.value")
-	tag.comment = "switch.tag"
-	emitStore(fn, tag, tagv, tagSource)
 
 	done := fn.newBasicBlock("switch.done")
 	if label != nil {
@@ -1553,20 +1703,12 @@ func (b *builder) switchStmtDynamic(fn *Function, s *ast.SwitchStmt, label *lblo
 		var nextCond *BasicBlock
 		for _, cond := range cc.List {
 			nextCond = fn.newBasicBlock("switch.next")
-			if tagv == kTrue {
-				// emit a proper if/else chain instead of a comparison
-				// of a value against true.
-				//
-				// NOTE(dh): adonovan had a todo saying "don't forget
-				// conversions though". As far as I can tell, there
-				// aren't any conversions that we need to take care of
-				// here. `case bool(a) && bool(b)` as well as `case
-				// bool(a && b)` are being taken care of by b.cond,
-				// and `case a` where a is not of type bool is
-				// invalid.
+			// For boolean switches, emit short-circuit control flow,
+			// just like an if/else-chain.
+			if tag == vTrue && !isNonTypeParamInterface(fn.info.Types[cond].Type) {
 				b.cond(fn, cond, body, nextCond)
 			} else {
-				cond := emitCompare(fn, token.EQL, emitLoad(fn, tag, cond), b.expr(fn, cond), cond)
+				cond := emitCompare(fn, token.EQL, tag, b.expr(fn, cond), cond)
 				emitIf(fn, cond, body, nextCond, cond.Source())
 			}
 
@@ -1601,6 +1743,8 @@ func (b *builder) switchStmtDynamic(fn *Function, s *ast.SwitchStmt, label *lblo
 	fn.currentBlock = done
 }
 
+// typeSwitchStmt emits to fn code for the type switch statement s, optionally
+// labelled by label.
 func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lblock) {
 	if s.Init != nil {
 		b.stmt(fn, s.Init)
@@ -1609,27 +1753,25 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
 	var tag Value
 	switch e := s.Assign.(type) {
 	case *ast.ExprStmt: // x.(type)
-		tag = b.expr(fn, unparen(e.X).(*ast.TypeAssertExpr).X)
+		tag = b.expr(fn, ast.Unparen(e.X).(*ast.TypeAssertExpr).X)
 	case *ast.AssignStmt: // y := x.(type)
-		tag = b.expr(fn, unparen(e.Rhs[0]).(*ast.TypeAssertExpr).X)
+		tag = b.expr(fn, ast.Unparen(e.Rhs[0]).(*ast.TypeAssertExpr).X)
 	default:
 		panic("unreachable")
 	}
-	tagPtr := emitLocal(fn, tag.Type(), tag.Source(), "")
-	emitStore(fn, tagPtr, tag, tag.Source())
 
 	// +1 in case there's no explicit default case
 	heads := make([]*BasicBlock, 0, len(s.Body.List)+1)
 
 	entry := fn.currentBlock
-	done := fn.newBasicBlock("done")
+	done := fn.newBasicBlock("typeswitch.done")
 	if label != nil {
 		label._break = done
 	}
 
 	// set up type switch and constant switch, populate their conditions
 	tswtch := &TypeSwitch{
-		Tag:   emitLoad(fn, tagPtr, tag.Source()),
+		Tag:   tag,
 		Conds: make([]types.Type, 0, len(s.Body.List)+1),
 	}
 	cswtch := &ConstantSwitch{
@@ -1641,7 +1783,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
 	var default_ *ast.CaseClause
 	for _, clause := range s.Body.List {
 		cc := clause.(*ast.CaseClause)
-		if obj, ok := fn.Pkg.info.Implicits[cc].(*types.Var); ok {
+		if obj, ok := fn.info.Implicits[cc].(*types.Var); ok {
 			emitLocalVar(fn, obj, cc)
 		}
 		if cc.List == nil {
@@ -1649,12 +1791,12 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
 			default_ = cc
 		} else {
 			for _, expr := range cc.List {
-				tswtch.Conds = append(tswtch.Conds, fn.Pkg.typeOf(expr))
-				cswtch.Conds = append(cswtch.Conds, emitConst(fn, intConst(int64(index), expr)))
+				tswtch.Conds = append(tswtch.Conds, fn.typeOf(expr))
+				cswtch.Conds = append(cswtch.Conds, intConst(int64(index), expr))
 				index++
 			}
 			if len(cc.List) == 1 {
-				rets = append(rets, fn.Pkg.typeOf(cc.List[0]))
+				rets = append(rets, fn.typeOf(cc.List[0]))
 			} else {
 				for range cc.List {
 					rets = append(rets, tag.Type())
@@ -1675,10 +1817,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
 	// default branch
 	fn.currentBlock = entry
 	fn.emit(tswtch, s)
-	cswtch.Conds = append(cswtch.Conds, emitConst(fn, intConst(int64(-1), nil)))
-	// in theory we should add a local and stores/loads for tswtch, to
-	// generate sigma nodes in the branches. however, there isn't any
-	// useful information we could possibly attach to it.
+	cswtch.Conds = append(cswtch.Conds, intConst(int64(-1), nil))
 	cswtch.Tag = emitExtract(fn, tswtch, 0, s)
 	fn.emit(cswtch, s)
 
@@ -1696,7 +1835,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
 			heads = append(heads, head)
 			fn.currentBlock = head
 
-			if obj, ok := fn.Pkg.info.Implicits[cc].(*types.Var); ok {
+			if obj, ok := fn.info.Implicits[cc].(*types.Var); ok {
 				// In a switch y := x.(type), each case clause
 				// implicitly declares a distinct object y.
 				// In a single-type case, y has that type.
@@ -1705,7 +1844,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
 
 				l := fn.vars[obj]
 				if rets[index] == tUntypedNil {
-					emitStore(fn, l, emitConst(fn, nilConst(tswtch.Tag.Type(), nil)), s.Assign)
+					emitStore(fn, l, nilConst(tswtch.Tag.Type(), nil), s.Assign)
 				} else {
 					x := emitExtract(fn, tswtch, index+1, s.Assign)
 					emitStore(fn, l, x, nil)
@@ -1736,7 +1875,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
 			tail:   fn.targets,
 			_break: done,
 		}
-		if obj, ok := fn.Pkg.info.Implicits[default_].(*types.Var); ok {
+		if obj, ok := fn.info.Implicits[default_].(*types.Var); ok {
 			l := fn.vars[obj]
 			x := emitExtract(fn, tswtch, index+1, s.Assign)
 			emitStore(fn, l, x, s)
@@ -1761,7 +1900,6 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (no
 		instr.setType(types.NewTuple(varIndex, varOk))
 		fn.emit(instr, s)
 		fn.emit(new(Unreachable), s)
-		addEdge(fn.currentBlock, fn.Exit)
 		return true
 	}
 
@@ -1806,7 +1944,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (no
 				Dir:  types.SendOnly,
 				Chan: ch,
 				Send: emitConv(fn, b.expr(fn, comm.Value),
-					typeutil.CoreType(ch.Type()).Underlying().(*types.Chan).Elem(), comm),
+					typeutil.CoreType(fn.typ(ch.Type())).(*types.Chan).Elem(), comm),
 				Pos: comm.Arrow,
 			}
 			if debugInfo {
@@ -1814,7 +1952,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (no
 			}
 
 		case *ast.AssignStmt: // x := <-ch
-			recv := unparen(comm.Rhs[0]).(*ast.UnaryExpr)
+			recv := ast.Unparen(comm.Rhs[0]).(*ast.UnaryExpr)
 			st = &SelectState{
 				Dir:  types.RecvOnly,
 				Chan: b.expr(fn, recv.X),
@@ -1825,7 +1963,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (no
 			}
 
 		case *ast.ExprStmt: // <-ch
-			recv := unparen(comm.X).(*ast.UnaryExpr)
+			recv := ast.Unparen(comm.X).(*ast.UnaryExpr)
 			st = &SelectState{
 				Dir:  types.RecvOnly,
 				Chan: b.expr(fn, recv.X),
@@ -1849,7 +1987,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (no
 	vars = append(vars, varIndex, varOk)
 	for _, st := range states {
 		if st.Dir == types.RecvOnly {
-			tElem := typeutil.CoreType(st.Chan.Type()).Underlying().(*types.Chan).Elem()
+			tElem := typeutil.CoreType(fn.typ(st.Chan.Type())).(*types.Chan).Elem()
 			vars = append(vars, anonVar(tElem))
 		}
 	}
@@ -1886,10 +2024,10 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (no
 			b.stmtList(fn, clause.Body)
 			emitJump(fn, done, s)
 			fn.targets = fn.targets.tail
-			swtch.Conds = append(swtch.Conds, emitConst(fn, intConst(-1, nil)))
+			swtch.Conds = append(swtch.Conds, intConst(-1, nil))
 			continue
 		}
-		swtch.Conds = append(swtch.Conds, emitConst(fn, intConst(int64(state), nil)))
+		swtch.Conds = append(swtch.Conds, intConst(int64(state), nil))
 		body := fn.newBasicBlock("select.body")
 		fn.currentBlock = body
 		bodies = append(bodies, body)
@@ -1947,23 +2085,23 @@ func (b *builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) {
 	// Use forStmtGo122 instead if it applies.
 	if s.Init != nil {
 		if assign, ok := s.Init.(*ast.AssignStmt); ok && assign.Tok == token.DEFINE {
-			if version.Compare(fn.goversion, "go1.22") >= 0 {
+			if versions.AtLeast(fn.goversion, versions.Go1_22) {
 				b.forStmtGo122(fn, s, label)
 				return
 			}
 		}
 	}
 
-	//	...init...
-	//      jump loop
+	//     ...init...
+	//     jump loop
 	// loop:
-	//      if cond goto body else done
+	//     if cond goto body else done
 	// body:
-	//      ...body...
-	//      jump post
-	// post:				 (target of continue)
-	//      ...post...
-	//      jump loop
+	//     ...body...
+	//     jump post
+	// post:                                 (target of continue)
+	//     ...post...
+	//     jump loop
 	// done:                                 (target of break)
 	if s.Init != nil {
 		b.stmt(fn, s.Init)
@@ -2052,9 +2190,10 @@ func (b *builder) forStmtGo122(fn *Function, s *ast.ForStmt, label *lblock) {
 	vars := make([]loopVar, len(init.Lhs))
 	for i, lhs := range init.Lhs {
 		v := identVar(fn, lhs.(*ast.Ident))
+		typ := fn.typ(v.Type())
 
 		fn.currentBlock = pre
-		outer := emitLocal(fn, v.Type(), lhs, v.Name())
+		outer := emitLocal(fn, typ, lhs, v.Name())
 
 		fn.currentBlock = loop
 		phi := &Phi{}
@@ -2066,7 +2205,7 @@ func (b *builder) forStmtGo122(fn *Function, s *ast.ForStmt, label *lblock) {
 		// If next is local, it reuses the address and zeroes the old value so
 		// load before allocating next.
 		load := emitLoad(fn, phi, init)
-		next := emitLocal(fn, v.Type(), lhs, v.Name())
+		next := emitLocal(fn, typ, lhs, v.Name())
 		store := emitStore(fn, next, load, s)
 
 		phi.Edges = []Value{outer, next} // pre edge is emitted before post edge.
@@ -2079,7 +2218,7 @@ func (b *builder) forStmtGo122(fn *Function, s *ast.ForStmt, label *lblock) {
 		fn.vars[v.obj] = v.outer
 	}
 	const isDef = false // assign to already-allocated outers
-	b.assignStmt(fn, init.Lhs, init.Rhs, isDef, s)
+	b.assignStmt(fn, init.Lhs, init.Rhs, isDef, init)
 	if label != nil {
 		label._break = done
 		label._continue = post
@@ -2163,8 +2302,8 @@ func (b *builder) forStmtGo122(fn *Function, s *ast.ForStmt, label *lblock) {
 		// Remove instructions for phi, load, and store.
 		// lift() will remove the unused i_next *Alloc.
 		isDead := func(i Instruction) bool { return dead[i] }
-		loop.Instrs = removeInstrsIf(loop.Instrs, isDead)
-		post.Instrs = removeInstrsIf(post.Instrs, isDead)
+		loop.Instrs = slices.DeleteFunc(loop.Instrs, isDead)
+		post.Instrs = slices.DeleteFunc(post.Instrs, isDead)
 	}
 }
 
@@ -2174,46 +2313,40 @@ func (b *builder) forStmtGo122(fn *Function, s *ast.ForStmt, label *lblock) {
 // forPos is the position of the "for" token.
 func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, source ast.Node) (k, v Value, loop, done *BasicBlock) {
 	//
-	//      length = len(x)
-	//      index = -1
-	// loop:                                   (target of continue)
-	//      index++
-	// 	if index < length goto body else done
+	//     length = len(x)
+	//     index = -1
+	// loop:                                     (target of continue)
+	//     index++
+	//     if index < length goto body else done
 	// body:
-	//      k = index
-	//      v = x[index]
-	//      ...body...
-	// 	jump loop
-	// done:                                   (target of break)
-
-	// We store in an Alloc and load it on each iteration so that lifting produces the necessary σ nodes
-	xAlloc := newVariable(fn, x.Type(), source)
-	xAlloc.store(x)
+	//     k = index
+	//     v = x[index]
+	//     ...body...
+	//     jump loop
+	// done:                                     (target of break)
 
 	// Determine number of iterations.
-	//
-	// We store the length in an Alloc and load it on each iteration so that lifting produces the necessary σ nodes
-	length := newVariable(fn, tInt, source)
-	if arr, ok := typeutil.CoreType(deref(x.Type())).(*types.Array); ok {
-		// For array or *array, the number of iterations is known statically thanks to the type. We avoid a data
-		// dependence upon x, permitting later dead-code elimination if x is pure, static unrolling, etc. Ranging over a
-		// nil *array may have >0 iterations. We still generate code for x, in case it has effects.
-		//
-		// We use the core type of x, even though the length of type parameters isn't constant as per the language
-		// specification. Just because len(x) isn't constant doesn't mean we can't emit IR that takes advantage of a
-		// known length.
-		length.store(emitConst(fn, intConst(arr.Len(), nil)))
+	var length Value
+	dt := deref(x.Type())
+	if arr, ok := typeutil.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.
+		length = intConst(arr.Len(), nil)
 	} else {
 		// length = len(x).
 		var c Call
 		c.Call.Value = makeLen(x.Type())
 		c.Call.Args = []Value{x}
 		c.setType(tInt)
-		length.store(fn.emit(&c, source))
+		length = fn.emit(&c, source)
 	}
 
 	index := emitLocal(fn, tInt, source, "rangeindex")
-	emitStore(fn, index, emitConst(fn, intConst(-1, nil)), source)
+	emitStore(fn, index, intConst(-1, nil), source)
 
 	loop = fn.newBasicBlock("rangeindex.loop")
 	emitJump(fn, loop, source)
@@ -2222,20 +2355,19 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, source ast.
 	incr := &BinOp{
 		Op: token.ADD,
 		X:  emitLoad(fn, index, source),
-		Y:  emitConst(fn, intConst(1, nil)),
+		Y:  vOne,
 	}
 	incr.setType(tInt)
 	emitStore(fn, index, fn.emit(incr, source), source)
 
 	body := fn.newBasicBlock("rangeindex.body")
 	done = fn.newBasicBlock("rangeindex.done")
-	emitIf(fn, emitCompare(fn, token.LSS, incr, length.load(), source), body, done, source)
+	emitIf(fn, emitCompare(fn, token.LSS, incr, length, source), body, done, source)
 	fn.currentBlock = body
 
 	k = emitLoad(fn, index, source)
 	if tv != nil {
-		x := xAlloc.load()
-		switch t := typeutil.CoreType(x.Type()).Underlying().(type) {
+		switch t := typeutil.CoreType(x.Type()).(type) {
 		case *types.Array:
 			instr := &Index{
 				X:     x,
@@ -2286,48 +2418,53 @@ func (b *builder) rangeIter(fn *Function, x Value, tk, tv types.Type, source ast
 	// done:                                   (target of break)
 	//
 
+	var ak, av types.Type
+	isString := false
+	if m, ok := typeutil.CoreType(x.Type()).(*types.Map); ok {
+		ak, av = m.Key(), m.Elem()
+	} else {
+		isString = true
+		ak, av = tInt, tRune
+	}
 	if tk == nil {
-		tk = tInvalid
+		ak = tInvalid
 	}
 	if tv == nil {
-		tv = tInvalid
+		av = tInvalid
 	}
 
 	rng := &Range{X: x}
 	rng.setType(typeutil.NewIterator(types.NewTuple(
 		varOk,
-		newVar("k", tk),
-		newVar("v", tv),
+		newVar("k", ak),
+		newVar("v", av),
 	)))
-	it := newVariable(fn, rng.typ, source)
-	it.store(fn.emit(rng, source))
+	it := fn.emit(rng, source)
 
 	loop = fn.newBasicBlock("rangeiter.loop")
 	emitJump(fn, loop, source)
 	fn.currentBlock = loop
 
-	// Go doesn't currently allow ranging over string|[]byte, so isString is decidable.
-	_, isString := typeutil.CoreType(x.Type()).Underlying().(*types.Basic)
-
-	okvInstr := &Next{
-		Iter:     it.load(),
+	okv := &Next{
+		Iter:     it,
 		IsString: isString,
 	}
-	okvInstr.setType(rng.typ.(*typeutil.Iterator).Elem())
-	fn.emit(okvInstr, source)
-	okv := newVariable(fn, okvInstr.Type(), source)
-	okv.store(okvInstr)
+	okv.setType(rng.typ.(*typeutil.Iterator).Elem())
+	fn.emit(okv, source)
 
 	body := fn.newBasicBlock("rangeiter.body")
 	done = fn.newBasicBlock("rangeiter.done")
-	emitIf(fn, emitExtract(fn, okv.load(), 0, source), body, done, source)
+	emitIf(fn, emitExtract(fn, okv, 0, source), body, done, source)
 	fn.currentBlock = body
 
-	if tk != tInvalid {
-		k = emitExtract(fn, okv.load(), 1, source)
+	// The assignment may widen a map or string
+	// key/value to a variable's interface type
+	// (cases #1 and #2 of go.dev/issue/78110).
+	if tk != nil {
+		k = emitConv(fn, emitExtract(fn, okv, 1, source), tk, source)
 	}
-	if tv != tInvalid {
-		v = emitExtract(fn, okv.load(), 2, source)
+	if tv != nil {
+		v = emitConv(fn, emitExtract(fn, okv, 2, source), tv, source)
 	}
 	return
 }
@@ -2353,16 +2490,14 @@ func (b *builder) rangeChan(fn *Function, x Value, tk types.Type, source ast.Nod
 	emitJump(fn, loop, source)
 	fn.currentBlock = loop
 
-	recv := emitRecv(fn, x, true, types.NewTuple(newVar("k", typeutil.CoreType(x.Type()).Underlying().(*types.Chan).Elem()), varOk), source)
-	retv := newVariable(fn, recv.Type(), source)
-	retv.store(recv)
+	retv := emitRecv(fn, x, true, types.NewTuple(newVar("k", typeutil.CoreType(x.Type()).(*types.Chan).Elem()), varOk), source)
 
 	body := fn.newBasicBlock("rangechan.body")
 	done = fn.newBasicBlock("rangechan.done")
-	emitIf(fn, emitExtract(fn, retv.load(), 1, source), body, done, source)
+	emitIf(fn, emitExtract(fn, retv, 1, source), body, done, source)
 	fn.currentBlock = body
 	if tk != nil {
-		k = emitExtract(fn, retv.load(), 0, source)
+		k = emitExtract(fn, retv, 0, source)
 	}
 	return
 }
@@ -2393,7 +2528,7 @@ func (b *builder) rangeInt(fn *Function, x Value, tk types.Type, source ast.Node
 
 	body := fn.newBasicBlock("rangeint.body")
 	done = fn.newBasicBlock("rangeint.done")
-	emitIf(fn, emitCompare(fn, token.LSS, emitConst(fn, zeroConst(T, source)), x, source), body, done, source)
+	emitIf(fn, emitCompare(fn, token.LSS, zeroConst(T, source), x, source), body, done, source)
 
 	loop = fn.newBasicBlock("rangeint.loop")
 	fn.currentBlock = loop
@@ -2401,7 +2536,7 @@ func (b *builder) rangeInt(fn *Function, x Value, tk types.Type, source ast.Node
 	incr := &BinOp{
 		Op: token.ADD,
 		X:  emitLoad(fn, iter, source),
-		Y:  emitConv(fn, emitConst(fn, intConst(1, source)), T, source),
+		Y:  emitConv(fn, intConst(1, source), T, source),
 	}
 	incr.setType(T)
 	emitStore(fn, iter, fn.emit(incr, source), source)
@@ -2418,41 +2553,15 @@ func (b *builder) rangeInt(fn *Function, x Value, tk types.Type, source ast.Node
 	return
 }
 
-type variable struct {
-	alloc  *Alloc
-	fn     *Function
-	source ast.Node
-}
-
-func newVariable(fn *Function, typ types.Type, source ast.Node) *variable {
-	alloc := &Alloc{}
-	alloc.setType(types.NewPointer(typ))
-	fn.emit(alloc, source)
-	fn.Locals = append(fn.Locals, alloc)
-	return &variable{
-		alloc:  alloc,
-		fn:     fn,
-		source: source,
-	}
-}
-
-func (v *variable) store(sv Value) {
-	emitStore(v.fn, v.alloc, sv, v.source)
-}
-
-func (v *variable) load() Value {
-	return emitLoad(v.fn, v.alloc, v.source)
-}
-
 // rangeStmt emits to fn code for the range statement s, optionally
 // labelled by label.
 func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock, source ast.Node) {
 	var tk, tv types.Type
 	if s.Key != nil && !isBlankIdent(s.Key) {
-		tk = fn.Pkg.typeOf(s.Key)
+		tk = fn.typeOf(s.Key)
 	}
 	if s.Value != nil && !isBlankIdent(s.Value) {
-		tv = fn.Pkg.typeOf(s.Value)
+		tv = fn.typeOf(s.Value)
 	}
 
 	// create locals for s.Key and s.Value
@@ -2470,8 +2579,7 @@ func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock, sourc
 		}
 	}
 
-	afterGo122 := version.Compare(fn.goversion, "go1.22") >= 0
-
+	afterGo122 := versions.AtLeast(fn.goversion, versions.Go1_22)
 	if s.Tok == token.DEFINE && !afterGo122 {
 		// pre-go1.22: If iteration variables are defined (:=), this
 		// occurs once outside the loop.
@@ -2482,7 +2590,7 @@ func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock, sourc
 
 	var k, v Value
 	var loop, done *BasicBlock
-	switch rt := typeutil.CoreType(x.Type()).Underlying().(type) {
+	switch rt := typeutil.CoreType(x.Type()).(type) {
 	case *types.Slice, *types.Array, *types.Pointer: // *array
 		k, v, loop, done = b.rangeIndexed(fn, x, tv, source)
 
@@ -2509,7 +2617,7 @@ func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock, sourc
 		//      for x := range f { ... }
 		// into
 		//      f(func(x T) bool { ... })
-		b.rangeFunc(fn, x, tk, tv, s, label)
+		b.rangeFunc(fn, x, s, label)
 		return
 
 	default:
@@ -2555,7 +2663,7 @@ func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock, sourc
 // rangeFunc emits to fn code for the range-over-func rng.Body of the iterator
 // function x, optionally labelled by label. It creates a new anonymous function
 // yield for rng and builds the function.
-func (b *builder) rangeFunc(fn *Function, x Value, tk, tv types.Type, rng *ast.RangeStmt, label *lblock) {
+func (b *builder) rangeFunc(fn *Function, x Value, rng *ast.RangeStmt, label *lblock) {
 	// Consider the SSA code for the outermost range-over-func in fn:
 	//
 	//   func fn(...) (ret R) {
@@ -2678,15 +2786,22 @@ func (b *builder) rangeFunc(fn *Function, x Value, tk, tv types.Type, rng *ast.R
 
 	/* synthetic yield function for body of range-over-func loop */
 	y := &Function{
-		name:         fmt.Sprintf("%s$%d", fn.Name(), anonIdx+1),
-		Signature:    ysig,
-		Synthetic:    SyntheticRangeOverFuncYield,
-		parent:       fn,
-		Pkg:          fn.Pkg,
-		Prog:         fn.Prog,
-		functionBody: new(functionBody),
-	}
-	y.source = rng
+		name:           fmt.Sprintf("%s$%d", fn.Name(), anonIdx+1),
+		Signature:      ysig,
+		Synthetic:      "range-over-func yield",
+		pos:            rng.Range,
+		parent:         fn,
+		anonIdx:        int32(len(fn.AnonFuncs)),
+		Pkg:            fn.Pkg,
+		Prog:           fn.Prog,
+		syntax:         rng,
+		info:           fn.info,
+		build:          (*builder).buildYieldFunc,
+		topLevelOrigin: nil,
+		typeparams:     fn.typeparams,
+		typeargs:       fn.typeargs,
+		subst:          fn.subst,
+	}
 	y.goversion = fn.goversion
 	y.jump = jump
 	y.deferstack = fn.deferstack
@@ -2704,7 +2819,7 @@ func (b *builder) rangeFunc(fn *Function, x Value, tk, tv types.Type, rng *ast.R
 	// * create new exit nodes in exits.
 	// (y is not marked 'built' until the end of the enclosing FuncDecl.)
 	unresolved := len(fn.exits)
-	b.buildYieldFunc(y)
+	y.build(b, y)
 	fn.uniq = y.uniq // resume after y's unique values
 
 	// Emit the call of y.
@@ -2755,28 +2870,25 @@ func (b *builder) buildYieldResume(fn *Function, jump *types.Var, exits []*exit,
 	bodies[1] = fn.newBasicBlock("rangefunc.resume.ready")
 
 	conds := make([]Value, 2, 2+len(exits))
-	conds[0] = emitConst(fn, jBusy())
-	conds[1] = emitConst(fn, jReady())
+	conds[0] = jBusy
+	conds[1] = jReady
 
 	fn.currentBlock = bodies[0]
 	fn.emit(
 		&Panic{
-			X: emitConv(fn, emitConst(fn, stringConst("iterator call did not preserve panic", nil)), tEface, nil),
+			X: emitConv(fn, jDroppedPanic, tEface, nil),
 		},
 		nil,
 	)
-	addEdge(fn.currentBlock, fn.Exit)
 
 	fn.currentBlock = bodies[1]
-	storeVar(fn, jump, emitConst(fn, jDone()), nil)
+	storeVar(fn, jump, jDone, nil)
 	emitJump(fn, done, nil)
 
 	for _, e := range exits {
 		body := fn.newBasicBlock(fmt.Sprintf("rangefunc.resume.exit.%d", e.id))
 		bodies = append(bodies, body)
-		id_ := intConst(e.id, nil)
-		id_.comment = fmt.Sprintf("rangefunc.exit.%d", e.id)
-		id := emitConst(fn, id_)
+		id := intConst(e.id, nil)
 		conds = append(conds, id)
 
 		fn.currentBlock = body
@@ -2797,15 +2909,17 @@ func (b *builder) buildYieldResume(fn *Function, jump *types.Var, exits []*exit,
 			// fn is a range-over-func function.
 
 			storeVar(fn, fn.jump, id, e.source)
-			vFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, e.source))
-			emitReturn(fn, []Value{vFalse}, e.source)
+			vFalse := NewConst(constant.MakeBool(false), tBool, e.source)
+			fn.emit(&Return{Results: []Value{vFalse}}, e.source)
 
 		case e.block == nil && e.label == nil: // return from fn?
 			// case EXIT(id): { return ... }
-
-			// The results have already been stored to variables in fn.results, so
-			// emitReturn doesn't have to do it again.
-			emitReturn(fn, nil, e.source)
+			fn.emit(new(RunDefers), e.source)
+			results := make([]Value, len(fn.results))
+			for i, r := range fn.results {
+				results[i] = emitLoad(fn, r, e.source)
+			}
+			fn.emit(&Return{Results: results}, e.source)
 
 		case e.block != nil:
 			// case EXIT(id): goto block
@@ -2874,7 +2988,7 @@ start:
 		instr := &Send{
 			Chan: b.expr(fn, s.Chan),
 			X: emitConv(fn, b.expr(fn, s.Value),
-				typeutil.CoreType(fn.Pkg.typeOf(s.Chan)).Underlying().(*types.Chan).Elem(), s),
+				typeutil.CoreType(fn.typeOf(s.Chan)).(*types.Chan).Elem(), s),
 		}
 		fn.emit(instr, s)
 
@@ -2884,7 +2998,7 @@ start:
 			op = token.SUB
 		}
 		loc := b.addr(fn, s.X, false)
-		b.assignOp(fn, loc, emitConst(fn, NewConst(constant.MakeInt64(1), loc.typ(), s)), op, s)
+		b.assignOp(fn, loc, NewConst(constant.MakeInt64(1), loc.typ(), s), op, s)
 
 	case *ast.AssignStmt:
 		switch s.Tok {
@@ -2907,11 +3021,14 @@ start:
 		// The "intrinsics" new/make/len/cap are forbidden here.
 		// panic is treated like an ordinary function call.
 		deferstack := emitLoad(fn, fn.lookup(fn.deferstack, false), s)
-		v := Defer{_DeferStack: deferstack}
+		v := Defer{DeferStack: deferstack}
 		b.setCall(fn, s.Call, &v.Call)
-		fn.hasDefer = true
 		fn.emit(&v, s)
 
+		// A deferred call can cause recovery from panic,
+		// and control resumes at the Recover block.
+		createRecoverBlock(fn.source)
+
 	case *ast.ReturnStmt:
 		b.returnStmt(fn, s)
 
@@ -2993,12 +3110,10 @@ func (b *builder) branchStmt(fn *Function, s *ast.BranchStmt) {
 	} else { // break outside of fn.
 		// fn must be a range-over-func
 		e := blockExit(fn, block, s)
-		id_ := intConst(e.id, s)
-		id_.comment = fmt.Sprintf("rangefunc.exit.%d", e.id)
-		id := emitConst(fn, id_)
+		id := intConst(e.id, s)
 		storeVar(fn, fn.jump, id, s)
-		vFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, s))
-		emitReturn(fn, []Value{vFalse}, s)
+		vFalse := NewConst(constant.MakeBool(false), tBool, s)
+		fn.emit(&Return{Results: []Value{vFalse}}, e.source)
 	}
 	fn.currentBlock = fn.newBasicBlock("unreachable")
 }
@@ -3009,7 +3124,7 @@ func (b *builder) returnStmt(fn *Function, s *ast.ReturnStmt) {
 
 	var results []Value
 
-	sig := fn.sourceFn.Signature // signature of the enclosing source function
+	sig := fn.source.Signature // signature of the enclosing source function
 
 	// Convert return operands to result type.
 	if len(s.Results) == 1 && sig.Results().Len() > 1 {
@@ -3032,7 +3147,7 @@ func (b *builder) returnStmt(fn *Function, s *ast.ReturnStmt) {
 	// Store the results.
 	for i, r := range results {
 		var result Value // fn.sourceFn.result[i] conceptually
-		if fn == fn.sourceFn {
+		if fn == fn.source {
 			result = fn.results[i]
 		} else { // lookup needed?
 			result = fn.lookup(fn.returnVars[i], false)
@@ -3045,86 +3160,107 @@ func (b *builder) returnStmt(fn *Function, s *ast.ReturnStmt) {
 		// The return statement is syntactically within the loop,
 		// but the generated code is in the 'switch jump {...}' after it.
 		e := returnExit(fn, s)
-		id_ := intConst(e.id, e.source)
-		id_.comment = fmt.Sprintf("rangefunc.exit.%d", e.id)
-		id := emitConst(fn, id_)
+		id := intConst(e.id, e.source)
 		storeVar(fn, fn.jump, id, e.source)
-		vFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, e.source))
-		emitReturn(fn, []Value{vFalse}, e.source)
+		vFalse := NewConst(constant.MakeBool(false), tBool, e.source)
+		fn.emit(&Return{Results: []Value{vFalse}}, e.source)
+		fn.currentBlock = fn.newBasicBlock("unreachable")
 		return
 	}
 
-	// The results have already been stored to variables in fn.results, so
-	// emitReturn doesn't have to do it again.
-	emitReturn(fn, nil, s)
+	// Run function calls deferred in this
+	// function when explicitly returning from it.
+	fn.emit(new(RunDefers), s)
+	// Reload (potentially) named result variables to form the result tuple.
+	results = results[:0]
+	for _, nr := range fn.results {
+		results = append(results, emitLoad(fn, nr, s))
+	}
+
+	fn.emit(&Return{Results: results}, s)
+	fn.currentBlock = fn.newBasicBlock("unreachable")
 }
 
-func emitReturn(fn *Function, results []Value, source ast.Node) {
-	for i, r := range results {
-		emitStore(fn, fn.results[i], r, source)
+// A buildFunc is a strategy for building the SSA body for a function.
+type buildFunc = func(*builder, *Function)
+
+// iterate causes all created but unbuilt functions to be built. As
+// this may create new methods, the process is iterated until it
+// converges.
+//
+// Waits for any dependencies to finish building.
+func (b *builder) iterate() {
+	for ; b.finished < len(b.fns); b.finished++ {
+		fn := b.fns[b.finished]
+		b.buildFunction(fn)
 	}
 
-	emitJump(fn, fn.Exit, source)
-	fn.currentBlock = fn.newBasicBlock("unreachable")
+	b.buildshared.markDone()
+	b.buildshared.wait()
 }
 
 // buildFunction builds IR code for the body of function fn.  Idempotent.
 func (b *builder) buildFunction(fn *Function) {
-	if fn.Blocks != nil {
-		return // building already started
+	if fn.build != nil {
+		assert(fn.parent == nil, "anonymous functions should not be built by buildFunction()")
+
+		if fn.Prog.mode&LogSource != 0 {
+			defer logStack("build %s @ %s", fn, fn.Prog.Fset.Position(fn.pos))()
+		}
+		fn.build(b, fn)
+		fn.done()
 	}
+}
 
-	var recvField *ast.FieldList
-	var body *ast.BlockStmt
-	var functype *ast.FuncType
-	switch n := fn.source.(type) {
-	case nil:
-		return // not a Go source function.  (Synthetic, or from object file.)
-	case *ast.FuncDecl:
-		functype = n.Type
-		recvField = n.Recv
-		body = n.Body
-	case *ast.FuncLit:
-		functype = n.Type
-		body = n.Body
-	default:
-		panic(n)
+// buildParamsOnly builds fn.Params from fn.Signature, but does not build fn.Body.
+func (b *builder) buildParamsOnly(fn *Function) {
+	// For external (C, asm) functions or functions loaded from
+	// export data, we must set fn.Params even though there is no
+	// body code to reference them.
+	if recv := fn.Signature.Recv(); recv != nil {
+		// TODO(dh): should we synthesize a node so we have position info?
+		fn.addParamVar(recv, nil)
+	}
+	params := fn.Signature.Params()
+	for i, n := 0, params.Len(); i < n; i++ {
+		// TODO(dh): should we synthesize a node so we have position info?
+		fn.addParamVar(params.At(i), nil)
 	}
 
-	if body == nil {
-		// External function.
-		if fn.Params == nil {
-			// This condition ensures we add a non-empty
-			// params list once only, but we may attempt
-			// the degenerate empty case repeatedly.
-			// TODO(adonovan): opt: don't do that.
+	// clear out other function state (keep consistent with finishBody)
+	fn.subst = nil
+}
 
-			// We set Function.Params even though there is no body
-			// code to reference them.  This simplifies clients.
-			if recv := fn.Signature.Recv(); recv != nil {
-				// XXX synthesize an ast.Node
-				fn.addParamVar(recv, nil)
-			}
-			params := fn.Signature.Params()
-			for i, n := 0, params.Len(); i < n; i++ {
-				// XXX synthesize an ast.Node
-				fn.addParamVar(params.At(i), nil)
-			}
+// buildFromSyntax builds fn.Body from fn.syntax, which must be non-nil.
+func (b *builder) buildFromSyntax(fn *Function) {
+	var (
+		recvField *ast.FieldList
+		body      *ast.BlockStmt
+		functype  *ast.FuncType
+	)
+	switch syntax := fn.syntax.(type) {
+	case *ast.FuncDecl:
+		functype = syntax.Type
+		recvField = syntax.Recv
+		body = syntax.Body
+		if body == nil {
+			b.buildParamsOnly(fn) // no body (non-Go function)
+			return
 		}
-		return
-	}
-	if fn.Prog.mode&LogSource != 0 {
-		defer logStack("build function %s @ %s", fn, fn.Prog.Fset.Position(fn.Pos()))()
+	case *ast.FuncLit:
+		functype = syntax.Type
+		body = syntax.Body
+	case nil:
+		panic("no syntax")
+	default:
+		panic(syntax) // unexpected syntax
 	}
-	fn.blocksets = b.blocksets
-	fn.Blocks = make([]*BasicBlock, 0, avgBlocks)
-	fn.sourceFn = fn
+	fn.source = fn
 	fn.startBody()
 	fn.createSyntacticParams(recvField, functype)
 	fn.createDeferStack()
-	fn.exitBlock()
 	b.stmt(fn, body)
-	if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) {
+	if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb == fn.Recover || cb.Preds != nil) {
 		// Control fell off the end of the function's body block.
 		//
 		// Block optimizations eliminate the current block, if
@@ -3132,15 +3268,10 @@ func (b *builder) buildFunction(fn *Function) {
 		// if this no-arg return is ill-typed for
 		// fn.Signature.Results, this block must be
 		// unreachable.  The sanity checker checks this.
-		// fn.emit(new(RunDefers))
-		// fn.emit(new(Return))
-		emitJump(fn, fn.Exit, nil)
+		fn.emit(new(RunDefers), nil)
+		fn.emit(new(Return), nil)
 	}
-	optimizeBlocks(fn)
-	buildFakeExits(fn)
 	fn.finishBody()
-	b.blocksets = fn.blocksets
-	fn.functionBody = nil
 }
 
 // buildYieldFunc builds the body of the yield function created
@@ -3150,7 +3281,7 @@ func (b *builder) buildYieldFunc(fn *Function) {
 	//
 	// In pseudo-Go this roughly builds:
 	// func yield(_k tk, _v tv) bool {
-	//         if jump != READY { panic("yield function called after range loop exit") }
+	// 	   if jump != READY { panic("yield function called after range loop exit") }
 	//     jump = BUSY
 	//     k, v = _k, _v // assign the iterator variable (if needed)
 	//     ... // rng.Body
@@ -3158,15 +3289,13 @@ func (b *builder) buildYieldFunc(fn *Function) {
 	//     jump = READY
 	//     return true
 	// }
-	s := fn.source.(*ast.RangeStmt)
-	fn.sourceFn = fn.parent.sourceFn
+	s := fn.syntax.(*ast.RangeStmt)
+	fn.source = fn.parent.source
 	fn.startBody()
 	params := fn.Signature.Params()
 	for v := range params.Variables() {
 		fn.addParamVar(v, nil)
 	}
-	fn.addResultVar(fn.Signature.Results().At(0), nil)
-	fn.exitBlock()
 
 	// Initial targets
 	ycont := fn.newBasicBlock("yield-continue")
@@ -3191,9 +3320,9 @@ func (b *builder) buildYieldFunc(fn *Function) {
 	//   return true
 	saved := fn.currentBlock
 	fn.currentBlock = ycont
-	storeVar(fn, fn.jump, emitConst(fn, jReady()), s.Body)
-	vTrue := emitConst(fn, NewConst(constant.MakeBool(true), tBool, nil))
-	emitReturn(fn, []Value{vTrue}, nil)
+	storeVar(fn, fn.jump, jReady, s.Body)
+	// A yield function's own deferstack is always empty, so rundefers is not needed.
+	fn.emit(&Return{Results: []Value{vTrue}}, nil)
 
 	// Emit header:
 	//
@@ -3205,26 +3334,25 @@ func (b *builder) buildYieldFunc(fn *Function) {
 	invalid := fn.newBasicBlock("yield-invalid")
 
 	jumpVal := emitLoad(fn, fn.lookup(fn.jump, true), nil)
-	emitIf(fn, emitCompare(fn, token.EQL, jumpVal, emitConst(fn, jReady()), nil), yloop, invalid, nil)
+	emitIf(fn, emitCompare(fn, token.EQL, jumpVal, jReady, nil), yloop, invalid, nil)
 	fn.currentBlock = invalid
 	fn.emit(
 		&Panic{
-			X: emitConv(fn, emitConst(fn, stringConst("yield function called after range loop exit", nil)), tEface, nil),
+			X: emitConv(fn, jLateYield, tEface, nil),
 		},
 		nil,
 	)
-	addEdge(fn.currentBlock, fn.Exit)
 
 	fn.currentBlock = yloop
-	storeVar(fn, fn.jump, emitConst(fn, jBusy()), s.Body)
+	storeVar(fn, fn.jump, jBusy, s.Body)
 
 	// Initialize k and v from params.
 	var tk, tv types.Type
 	if s.Key != nil && !isBlankIdent(s.Key) {
-		tk = fn.Pkg.typeOf(s.Key) // fn.parent.typeOf is identical
+		tk = fn.typeOf(s.Key) // fn.parent.typeOf is identical
 	}
 	if s.Value != nil && !isBlankIdent(s.Value) {
-		tv = fn.Pkg.typeOf(s.Value)
+		tv = fn.typeOf(s.Value)
 	}
 	if s.Tok == token.DEFINE {
 		if tk != nil {
@@ -3257,12 +3385,13 @@ func (b *builder) buildYieldFunc(fn *Function) {
 
 	// Build the body of the range loop.
 	b.stmt(fn, s.Body)
-	if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) {
+	if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb == fn.Recover || cb.Preds != nil) {
 		// Control fell off the end of the function's body block.
 		// Block optimizations eliminate the current block, if
 		// unreachable.
 		emitJump(fn, ycont, nil)
 	}
+	// pop the stack for the yield function
 	fn.targets = fn.targets.tail
 
 	// Clean up exits and promote any unresolved exits to fn.parent.
@@ -3280,12 +3409,10 @@ func (b *builder) buildYieldFunc(fn *Function) {
 			//     jump = id
 			//     return false
 			fn.currentBlock = lb._goto
-			id_ := intConst(e.id, e.source)
-			id_.comment = fmt.Sprintf("rangefunc.exit.%d", e.id)
-			id := emitConst(fn, id_)
+			id := intConst(e.id, e.source)
 			storeVar(fn, fn.jump, id, e.source)
-			vFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, e.source))
-			emitReturn(fn, []Value{vFalse}, e.source)
+			vFalse := NewConst(constant.MakeBool(false), tBool, e.source)
+			fn.emit(&Return{Results: []Value{vFalse}}, e.source)
 		}
 
 		if e.to != fn { // e needs to be handled by the parent too.
@@ -3296,38 +3423,54 @@ func (b *builder) buildYieldFunc(fn *Function) {
 	fn.finishBody()
 }
 
-// buildFuncDecl builds IR code for the function or method declared
-// by decl in package pkg.
-func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {
-	id := decl.Name
-	fn := pkg.values[pkg.info.Defs[id]].(*Function)
-	if decl.Recv == nil && id.Name == "init" {
-		var v Call
-		v.Call.Value = fn
-		v.setType(types.NewTuple())
-		pkg.init.emit(&v, decl)
-	}
-	fn.source = decl
-	b.buildFunction(fn)
+// addMakeInterfaceType records non-interface type t as the type of
+// the operand a MakeInterface operation, for [Program.RuntimeTypes].
+//
+// Acquires prog.makeInterfaceTypesMu.
+func addMakeInterfaceType(prog *Program, t types.Type) {
+	prog.makeInterfaceTypesMu.Lock()
+	defer prog.makeInterfaceTypesMu.Unlock()
+	if prog.makeInterfaceTypes == nil {
+		prog.makeInterfaceTypes = make(map[types.Type]unit)
+	}
+	prog.makeInterfaceTypes[t] = unit{}
 }
 
 // Build calls Package.Build for each package in prog.
+// Building occurs in parallel unless the BuildSerially mode flag was set.
 //
 // Build is intended for whole-program analysis; a typical compiler
 // need only build a single package.
 //
 // Build is idempotent and thread-safe.
 func (prog *Program) Build() {
+	var wg sync.WaitGroup
 	for _, p := range prog.packages {
-		p.Build()
+		if prog.mode&BuildSerially != 0 {
+			p.Build()
+		} else {
+			wg.Add(1)
+			cpuLimit <- unit{} // acquire a token
+			go func(p *Package) {
+				p.Build()
+				wg.Done()
+				<-cpuLimit // release a token
+			}(p)
+		}
 	}
+	wg.Wait()
 }
 
+// cpuLimit is a counting semaphore to limit CPU parallelism.
+var cpuLimit = make(chan unit, runtime.GOMAXPROCS(0))
+
 // Build builds IR code for all functions and vars in package p.
 //
-// Precondition: CreatePackage must have been called for all of p's
-// direct imports (and hence its direct imports must have been
-// error-free).
+// CreatePackage must have been called for all of p's direct imports
+// (and hence its direct imports must have been error-free). It is not
+// necessary to call CreatePackage for indirect dependencies.
+// Functions will be created for all necessary methods in those
+// packages on demand.
 //
 // Build is idempotent and thread-safe.
 func (p *Package) Build() { p.buildOnce.Do(p.build) }
@@ -3336,49 +3479,58 @@ func (p *Package) build() {
 	if p.info == nil {
 		return // synthetic package, e.g. "testmain"
 	}
-
-	// Ensure we have runtime type info for all exported members.
-	// TODO(adonovan): ideally belongs in memberFromObject, but
-	// that would require package creation in topological order.
-	for name, mem := range p.Members {
-		if ast.IsExported(name) {
-			p.Prog.needMethodsOf(mem.Type())
-		}
-	}
 	if p.Prog.mode&LogSource != 0 {
 		defer logStack("build %s", p)()
 	}
-	init := p.init
+
+	b := builder{fns: p.created}
+	b.iterate()
+
+	// We no longer need transient information: ASTs or go/types deductions.
+	p.info = nil
+	p.created = nil
+	p.files = nil
+	p.initVersion = nil
+
+	if p.Prog.mode&SanityCheckFunctions != 0 {
+		sanityCheckPackage(p)
+	}
+}
+
+// buildPackageInit builds fn.Body for the synthetic package initializer.
+func (b *builder) buildPackageInit(init *Function) {
+	p := init.Pkg
 	init.startBody()
-	init.exitBlock()
 
 	var done *BasicBlock
 
-	// Make init() skip if package is already initialized.
-	initguard := p.Var("init$guard")
-	doinit := init.newBasicBlock("init.start")
-	done = init.Exit
-	emitIf(init, emitLoad(init, initguard, nil), done, doinit, nil)
-	init.currentBlock = doinit
-	emitStore(init, initguard, emitConst(init, NewConst(constant.MakeBool(true), tBool, nil)), nil)
+	if p.Prog.mode&BareInits == 0 {
+		// Make init() skip if package is already initialized.
+		initguard := p.Var("init$guard")
+		doinit := init.newBasicBlock("init.start")
+		done = init.newBasicBlock("init.done")
+		emitIf(init, emitLoad(init, initguard, nil), done, doinit, nil)
+		init.currentBlock = doinit
+		emitStore(init, initguard, vTrue, nil)
 
-	// Call the init() function of each package we import.
-	for _, pkg := range p.Pkg.Imports() {
-		prereq := p.Prog.packages[pkg]
-		if prereq == nil {
-			panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Pkg.Path(), pkg.Path()))
+		// Call the init() function of each package we import.
+		for _, pkg := range p.Pkg.Imports() {
+			prereq := p.Prog.packages[pkg]
+			if prereq == nil {
+				panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Pkg.Path(), pkg.Path()))
+			}
+			var v Call
+			v.Call.Value = prereq.init
+			v.setType(types.NewTuple())
+			init.emit(&v, nil)
 		}
-		var v Call
-		v.Call.Value = prereq.init
-		v.setType(types.NewTuple())
-		init.emit(&v, nil)
 	}
 
-	b := builder{
-		printFunc: p.printFunc,
+	// Initialize package-level vars in correct order.
+	if len(p.info.InitOrder) > 0 && len(p.files) == 0 {
+		panic("no source files provided for package. cannot initialize globals")
 	}
 
-	// Initialize package-level vars in correct order.
 	for _, varinit := range p.info.InitOrder {
 		if init.Prog.mode&LogSource != 0 {
 			fmt.Fprintf(os.Stderr, "build global initializer %v @ %s\n",
@@ -3411,48 +3563,33 @@ func (p *Package) build() {
 			}
 		}
 	}
-	init.goversion = "" // The rest of the init function is synthetic. No syntax => no goversion.
 
-	// Build all package-level functions, init functions
-	// and methods, including unreachable/blank ones.
-	// We build them in source order, but it's not significant.
+	// The rest of the init function is synthetic:
+	// no syntax, info, goversion.
+	init.info = nil
+	init.goversion = ""
+
+	// Call all of the declared init() functions in source order.
 	for _, file := range p.files {
 		for _, decl := range file.Decls {
 			if decl, ok := decl.(*ast.FuncDecl); ok {
-				b.buildFuncDecl(p, decl)
+				id := decl.Name
+				if !isBlankIdent(id) && id.Name == "init" && decl.Recv == nil {
+					declaredInit := p.values[p.info.Defs[id]].(*Function)
+					var v Call
+					v.Call.Value = declaredInit
+					v.setType(types.NewTuple())
+					p.init.emit(&v, nil)
+				}
 			}
 		}
 	}
 
 	// Finish up init().
-	emitJump(init, done, nil)
-	init.finishBody()
-
-	// We no longer need ASTs or go/types deductions.
-	p.info = nil
-	p.initVersion = nil
-
-	if p.Prog.mode&SanityCheckFunctions != 0 {
-		sanityCheckPackage(p)
+	if p.Prog.mode&BareInits == 0 {
+		emitJump(init, done, nil)
+		init.currentBlock = done
 	}
-}
-
-// Like ObjectOf, but panics instead of returning nil.
-// Only valid during p's create and build phases.
-func (p *Package) objectOf(id *ast.Ident) types.Object {
-	if o := p.info.ObjectOf(id); o != nil {
-		return o
-	}
-	panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s",
-		id.Name, p.Prog.Fset.Position(id.Pos())))
-}
-
-// Like TypeOf, but panics instead of returning nil.
-// Only valid during p's create and build phases.
-func (p *Package) typeOf(e ast.Expr) types.Type {
-	if T := p.info.TypeOf(e); T != nil {
-		return T
-	}
-	panic(fmt.Sprintf("no type for %T @ %s",
-		e, p.Prog.Fset.Position(e.Pos())))
+	init.emit(new(Return), nil)
+	init.finishBody()
 }
diff --git a/vendor/honnef.co/go/tools/go/ir/const.go b/vendor/honnef.co/go/tools/go/ir/const.go
index 7d27ec57..399eb7a1 100644
--- a/vendor/honnef.co/go/tools/go/ir/const.go
+++ b/vendor/honnef.co/go/tools/go/ir/const.go
@@ -7,24 +7,58 @@ package ir
 // This file defines the Const SSA value type.
 
 import (
+	"bytes"
 	"fmt"
 	"go/ast"
 	"go/constant"
 	"go/types"
 	"strconv"
-	"strings"
 
 	"golang.org/x/exp/typeparams"
-	"honnef.co/go/tools/go/types/typeutil"
+	"honnef.co/go/tools/internal/xtools-internal/typesinternal"
 )
 
+// soleTypeKind returns a BasicInfo for which constant.Value can
+// represent all zero values for the types in the type set.
+//
+//	types.IsBoolean for false is a representative.
+//	types.IsInteger for 0
+//	types.IsString for ""
+//	0 otherwise.
+func soleTypeKind(typ types.Type) types.BasicInfo {
+	// State records the set of possible zero values (false, 0, "").
+	// Candidates (perhaps all) are eliminated during the type-set
+	// iteration, which executes at least once.
+	state := types.IsBoolean | types.IsInteger | types.IsString
+	underIs(typ, func(ut types.Type) bool {
+		var c types.BasicInfo
+		if t, ok := ut.(*types.Basic); ok {
+			c = t.Info()
+		}
+		if c&types.IsNumeric != 0 { // int/float/complex
+			c = types.IsInteger
+		}
+		state = state & c
+		return state != 0
+	})
+	return state
+}
+
 // NewConst returns a new constant of the specified value and type.
 // val must be valid according to the specification of Const.Value.
 func NewConst(val constant.Value, typ types.Type, source ast.Node) *Const {
+	if val == nil {
+		switch soleTypeKind(typ) {
+		case types.IsBoolean:
+			val = constant.MakeBool(false)
+		case types.IsInteger:
+			val = constant.MakeInt64(0)
+		case types.IsString:
+			val = constant.MakeString("")
+		}
+	}
 	c := &Const{
-		register: register{
-			typ: typ,
-		},
+		typ:   typ,
 		Value: val,
 	}
 	c.setSource(source)
@@ -50,95 +84,25 @@ func stringConst(s string, source ast.Node) *Const {
 
 // zeroConst returns a new "zero" constant of the specified type.
 func zeroConst(t types.Type, source ast.Node) Constant {
-	if _, ok := t.Underlying().(*types.Interface); ok && !typeparams.IsTypeParam(t) {
-		// Handle non-generic interface early to simplify following code.
-		return nilConst(t, source)
-	}
-
-	tset := typeutil.NewTypeSet(t)
-
-	switch typ := tset.CoreType().(type) {
-	case *types.Struct:
-		values := make([]Value, typ.NumFields())
-		for i := 0; i < typ.NumFields(); i++ {
-			values[i] = zeroConst(typ.Field(i).Type(), source)
-		}
-		ac := &AggregateConst{
-			register: register{typ: t},
-			Values:   values,
-		}
-		ac.setSource(source)
-		return ac
-	case *types.Tuple:
-		values := make([]Value, typ.Len())
-		for i := 0; i < typ.Len(); i++ {
-			values[i] = zeroConst(typ.At(i).Type(), source)
-		}
-		ac := &AggregateConst{
-			register: register{typ: t},
-			Values:   values,
-		}
-		ac.setSource(source)
-		return ac
-	}
-
-	isNillable := func(term *types.Term) bool {
-		switch typ := term.Type().Underlying().(type) {
-		case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature, *typeutil.Iterator:
-			return true
-		case *types.Basic:
-			switch typ.Kind() {
-			case types.UnsafePointer, types.UntypedNil:
-				return true
-			default:
-				return false
-			}
-		default:
-			return false
-		}
-	}
-
-	isInfo := func(info types.BasicInfo) func(*types.Term) bool {
-		return func(term *types.Term) bool {
-			basic, ok := term.Type().Underlying().(*types.Basic)
-			if !ok {
-				return false
-			}
-			return (basic.Info() & info) != 0
-		}
-	}
-
-	isArray := func(term *types.Term) bool {
-		_, ok := term.Type().Underlying().(*types.Array)
-		return ok
-	}
-
-	switch {
-	case tset.All(isInfo(types.IsNumeric)):
-		return NewConst(constant.MakeInt64(0), t, source)
-	case tset.All(isInfo(types.IsString)):
-		return NewConst(constant.MakeString(""), t, source)
-	case tset.All(isInfo(types.IsBoolean)):
-		return NewConst(constant.MakeBool(false), t, source)
-	case tset.All(isNillable):
-		return nilConst(t, source)
-	case tset.All(isArray):
-		var k ArrayConst
-		k.setType(t)
-		k.setSource(source)
-		return &k
-	default:
-		var k GenericConst
-		k.setType(t)
-		k.setSource(source)
-		return &k
-	}
+	return NewConst(nil, t, source)
 }
 
 func (c *Const) RelString(from *types.Package) string {
 	var p string
 	if c.Value == nil {
-		p = "nil"
+		switch c.typ.(type) {
+		case *types.Array, *types.Struct:
+			p = "{}"
+		case *types.Alias:
+			switch c.typ.Underlying().(type) {
+			case *types.Array, *types.Struct:
+				p = "{}"
+			default:
+				p, _ = typesinternal.ZeroString(types.Unalias(c.typ), types.RelativeTo(from))
+			}
+		default:
+			p, _ = typesinternal.ZeroString(c.typ, types.RelativeTo(from))
+		}
 	} else if c.Value.Kind() == constant.String {
 		v := constant.StringVal(c.Value)
 		const max = 20
@@ -150,58 +114,83 @@ func (c *Const) RelString(from *types.Package) string {
 	} else {
 		p = c.Value.String()
 	}
-	return fmt.Sprintf("Const <%s> {%s}", relType(c.Type(), from), p)
+	return p + ":" + relType(c.Type(), from)
+}
+
+func (c *Const) Name() string {
+	return c.RelString(nil)
 }
 
 func (c *Const) String() string {
-	if c.block == nil {
-		// Constants don't have a block till late in the compilation process. But we want to print consts during
-		// debugging.
-		return c.RelString(nil)
-	}
-	return c.RelString(c.Parent().pkg())
+	return c.RelString(nil)
 }
 
-func (v *ArrayConst) RelString(pkg *types.Package) string {
-	return fmt.Sprintf("ArrayConst <%s>", relType(v.Type(), pkg))
+func (c *Const) Type() types.Type {
+	return c.typ
 }
 
-func (v *ArrayConst) String() string {
-	return v.RelString(v.Parent().pkg())
+func (c *Const) Referrers() *[]Instruction {
+	return nil
 }
 
+func (c *Const) Parent() *Function { return nil }
+
 func (v *AggregateConst) RelString(pkg *types.Package) string {
-	values := make([]string, len(v.Values))
-	for i, v := range v.Values {
-		if v != nil {
-			values[i] = v.Name()
-		} else {
-			values[i] = "nil"
+	var b bytes.Buffer
+	fmt.Fprint(&b, "const {")
+	for i, vv := range v.Values {
+		if i > 0 {
+			fmt.Fprint(&b, ", ")
 		}
+		fmt.Fprint(&b, relName(vv, v))
 	}
-	return fmt.Sprintf("AggregateConst <%s> (%s)", relType(v.Type(), pkg), strings.Join(values, ", "))
+	fmt.Fprint(&b, "}")
+	return b.String()
+}
+
+func (v *AggregateConst) Name() string {
+	return v.RelString(nil)
 }
 
 func (v *AggregateConst) String() string {
-	if v.block == nil {
-		return v.RelString(nil)
-	}
-	return v.RelString(v.Parent().pkg())
+	return v.RelString(nil)
 }
 
-func (v *GenericConst) RelString(pkg *types.Package) string {
-	return fmt.Sprintf("GenericConst <%s>", relType(v.Type(), pkg))
+func (v *AggregateConst) Type() types.Type {
+	return v.typ
 }
 
-func (v *GenericConst) String() string {
-	return v.RelString(v.Parent().pkg())
+func (v *AggregateConst) Referrers() *[]Instruction {
+	return nil
 }
 
+func (v *AggregateConst) Parent() *Function { return nil }
+
 // IsNil returns true if this constant represents a typed or untyped nil value.
 func (c *Const) IsNil() bool {
-	return c.Value == nil
+	return c.Value == nil && nillable(c.typ)
+}
+
+// nillable reports whether *new(T) == nil is legal for type T.
+func nillable(t types.Type) bool {
+	if typeparams.IsTypeParam(t) {
+		return underIs(t, func(u types.Type) bool {
+			// empty type set (u==nil) => any underlying types => not nillable
+			return u != nil && nillable(u)
+		})
+	}
+	switch t.Underlying().(type) {
+	case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature:
+		return true
+	case *types.Interface:
+		return true // basic interface.
+	default:
+		return false
+	}
 }
 
+// TODO(adonovan): move everything below into honnef.co/go/tools/go/ir/interp.
+
 // Int64 returns the numeric value of this constant truncated to fit
 // a signed 64-bit integer.
 func (c *Const) Int64() int64 {
@@ -237,61 +226,16 @@ func (c *Const) Uint64() uint64 {
 // Float64 returns the numeric value of this constant truncated to fit
 // a float64.
 func (c *Const) Float64() float64 {
-	f, _ := constant.Float64Val(c.Value)
+	x := constant.ToFloat(c.Value) // (c.Value == nil) => x.Kind() == Unknown
+	f, _ := constant.Float64Val(x)
 	return f
 }
 
 // Complex128 returns the complex value of this constant truncated to
 // fit a complex128.
 func (c *Const) Complex128() complex128 {
-	re, _ := constant.Float64Val(constant.Real(c.Value))
-	im, _ := constant.Float64Val(constant.Imag(c.Value))
+	x := constant.ToComplex(c.Value) // (c.Value == nil) => x.Kind() == Unknown
+	re, _ := constant.Float64Val(constant.Real(x))
+	im, _ := constant.Float64Val(constant.Imag(x))
 	return complex(re, im)
 }
-
-func (c *Const) equal(o Constant) bool {
-	// TODO(dh): don't use == for types, this will miss identical pointer types, among others
-	oc, ok := o.(*Const)
-	if !ok {
-		return false
-	}
-	return c.typ == oc.typ && c.Value == oc.Value && c.source == oc.source
-}
-
-func (c *AggregateConst) equal(o Constant) bool {
-	oc, ok := o.(*AggregateConst)
-	if !ok {
-		return false
-	}
-	// TODO(dh): don't use == for types, this will miss identical pointer types, among others
-	if c.typ != oc.typ {
-		return false
-	}
-	if c.source != oc.source {
-		return false
-	}
-	for i, v := range c.Values {
-		if !v.(Constant).equal(oc.Values[i].(Constant)) {
-			return false
-		}
-	}
-	return true
-}
-
-func (c *ArrayConst) equal(o Constant) bool {
-	oc, ok := o.(*ArrayConst)
-	if !ok {
-		return false
-	}
-	// TODO(dh): don't use == for types, this will miss identical pointer types, among others
-	return c.typ == oc.typ && c.source == oc.source
-}
-
-func (c *GenericConst) equal(o Constant) bool {
-	oc, ok := o.(*GenericConst)
-	if !ok {
-		return false
-	}
-	// TODO(dh): don't use == for types, this will miss identical pointer types, among others
-	return c.typ == oc.typ && c.source == oc.source
-}
diff --git a/vendor/honnef.co/go/tools/go/ir/create.go b/vendor/honnef.co/go/tools/go/ir/create.go
index 046ffb75..f5656e22 100644
--- a/vendor/honnef.co/go/tools/go/ir/create.go
+++ b/vendor/honnef.co/go/tools/go/ir/create.go
@@ -12,35 +12,35 @@ import (
 	"go/ast"
 	"go/token"
 	"go/types"
-	"go/version"
 	"os"
 	"sync"
 
-	"honnef.co/go/tools/go/types/typeutil"
+	"honnef.co/go/tools/internal/xtools-internal/versions"
 )
 
-// measured on the standard library and rounded up to powers of two,
-// on average there are 8 blocks and 16 instructions per block in a
-// function.
-const avgBlocks = 8
-const avgInstructionsPerBlock = 16
-
 // NewProgram returns a new IR Program.
 //
 // mode controls diagnostics and checking during IR construction.
+//
+// To construct an SSA program:
+//
+//   - Call NewProgram to create an empty Program.
+//   - Call CreatePackage providing typed syntax for each package
+//     you want to build, and call it with types but not
+//     syntax for each of those package's direct dependencies.
+//   - Call [Package.Build] on each syntax package you wish to build,
+//     or [Program.Build] to build all of them.
+//
+// See the Example tests for simple examples.
 func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
-	prog := &Program{
+	return &Program{
 		Fset:     fset,
 		imported: make(map[string]*Package),
 		packages: make(map[*types.Package]*Package),
 		mode:     mode,
+		canon:    newCanonizer(),
+		ctxt:     types.NewContext(),
 	}
-
-	h := typeutil.MakeHasher() // protected by methodsMu, in effect
-	prog.methodSets.SetHasher(h)
-	prog.canon.SetHasher(h)
-
-	return prog
 }
 
 // memberFromObject populates package pkg with a member for the
@@ -71,7 +71,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion
 			Value:  NewConst(obj.Val(), obj.Type(), syntax),
 			pkg:    pkg,
 		}
-		pkg.values[obj] = c.Value
+		pkg.values[obj] = c
 		if name != "_" {
 			pkg.Members[name] = c
 		}
@@ -83,6 +83,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion
 			object: obj,
 			typ:    types.NewPointer(obj.Type()), // address
 		}
+		g.source = syntax
 		pkg.values[obj] = g
 		if name != "_" {
 			pkg.Members[name] = g
@@ -94,30 +95,9 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion
 			pkg.ninit++
 			name = fmt.Sprintf("init#%d", pkg.ninit)
 		}
-		fn := &Function{
-			name:      name,
-			object:    obj,
-			Signature: sig,
-			Pkg:       pkg,
-			Prog:      pkg.Prog,
-			goversion: goversion,
-		}
-
-		fn.source = syntax
-		fn.initHTML(pkg.printFunc)
-		if syntax == nil {
-			fn.Synthetic = SyntheticLoadedFromExportData
-		} else {
-			// Note: we initialize fn.Blocks in
-			// (*builder).buildFunction and not here because Blocks
-			// being nil is used to indicate that building of the
-			// function hasn't started yet.
-
-			fn.functionBody = &functionBody{
-				scratchInstructions: make([]Instruction, avgBlocks*avgInstructionsPerBlock),
-			}
-		}
-
+		fn := createFunction(pkg.Prog, obj, name, syntax, pkg.info, goversion)
+		fn.Pkg = pkg
+		pkg.created = append(pkg.created, fn)
 		pkg.values[obj] = fn
 		pkg.Functions = append(pkg.Functions, fn)
 		if name != "_" && sig.Recv() == nil {
@@ -129,6 +109,37 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion
 	}
 }
 
+// createFunction creates a function or method. It supports both
+// CreatePackage (with or without syntax) and the on-demand creation
+// of methods in non-created packages based on their types.Func.
+func createFunction(prog *Program, obj *types.Func, name string, syntax ast.Node, info *types.Info, goversion string) *Function {
+	sig := obj.Type().(*types.Signature)
+
+	/* declared function/method (from syntax or export data) */
+	fn := &Function{
+		name:           name,
+		object:         obj,
+		Signature:      sig,
+		build:          (*builder).buildFromSyntax,
+		info:           info,
+		goversion:      goversion,
+		pos:            obj.Pos(),
+		syntax:         syntax,
+		Pkg:            nil, // may be set by caller
+		Prog:           prog,
+		recvtypeparams: sig.RecvTypeParams(),
+		typeparams:     sig.TypeParams(),
+	}
+	if syntax == nil {
+		fn.Synthetic = "from type information"
+		fn.build = (*builder).buildParamsOnly
+	}
+	if fn.hasTypeParams() {
+		fn.generic = new(generic)
+	}
+	return fn
+}
+
 // membersFromDecl populates package pkg with members for each
 // typechecker object (var, func, const or type) associated with the
 // specified decl.
@@ -162,20 +173,11 @@ func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) {
 
 	case *ast.FuncDecl:
 		id := decl.Name
-		obj, ok := pkg.info.Defs[id]
-		if !ok {
-			panic(fmt.Sprintf("couldn't find object for id %q at %s",
-				id.Name, pkg.Prog.Fset.PositionFor(id.Pos(), false)))
-		}
-		if obj == nil {
-			panic(fmt.Sprintf("found nil object for id %q at %s",
-				id.Name, pkg.Prog.Fset.PositionFor(id.Pos(), false)))
-		}
-		memberFromObject(pkg, obj, decl, goversion)
+		memberFromObject(pkg, pkg.info.Defs[id], decl, goversion)
 	}
 }
 
-// CreatePackage constructs and returns an IR Package from the
+// CreatePackage creates and returns an IR Package from the
 // specified type-checked, error-free file ASTs, and populates its
 // Members mapping.
 //
@@ -183,40 +185,43 @@ func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) {
 // subsequent call to ImportedPackage(pkg.Path()).
 //
 // The real work of building IR form for each function is not done
-// until a subsequent call to Package.Build().
+// until a subsequent call to Package.Build.
 func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
+	if pkg == nil {
+		panic("nil pkg") // otherwise pkg.Scope below returns types.Universe!
+	}
 	p := &Package{
 		Prog:    prog,
 		Members: make(map[string]Member),
-		values:  make(map[types.Object]Value),
+		values:  make(map[types.Object]Member),
 		Pkg:     pkg,
-		// transient values (CREATE and BUILD phases)
+		syntax:  info != nil,
+		// transient values (cleared after Package.Build)
 		info:        info,
 		files:       files,
-		printFunc:   prog.PrintFunc,
 		initVersion: make(map[ast.Expr]string),
 	}
 
-	// Add init() function.
+	/* synthesized package initializer */
 	p.init = &Function{
-		name:         "init",
-		Signature:    new(types.Signature),
-		Synthetic:    SyntheticPackageInitializer,
-		Pkg:          p,
-		Prog:         prog,
-		functionBody: new(functionBody),
-		goversion:    "", // See Package.build for details.
+		name:      "init",
+		Signature: new(types.Signature),
+		Synthetic: "package initializer",
+		Pkg:       p,
+		Prog:      prog,
+		build:     (*builder).buildPackageInit,
+		info:      p.info,
+		goversion: "", // See Package.build for details.
 	}
-	p.init.initHTML(prog.PrintFunc)
 	p.Members[p.init.name] = p.init
 	p.Functions = append(p.Functions, p.init)
+	p.created = append(p.created, p.init)
 
-	// CREATE phase.
 	// Allocate all package members: vars, funcs, consts and types.
 	if len(files) > 0 {
 		// Go source package.
 		for _, file := range files {
-			goversion := version.Lang(p.info.FileVersions[file])
+			goversion := versions.Lang(versions.FileVersion(p.info, file))
 			for _, decl := range file.Decls {
 				membersFromDecl(p, decl, goversion)
 			}
@@ -230,6 +235,7 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *
 			obj := scope.Lookup(name)
 			memberFromObject(p, obj, nil, "")
 			if obj, ok := obj.(*types.TypeName); ok {
+				// No Unalias: aliases should not duplicate methods.
 				if named, ok := obj.Type().(*types.Named); ok {
 					for i, n := 0, named.NumMethods(); i < n; i++ {
 						memberFromObject(p, named.Method(i), nil, "")
@@ -239,13 +245,15 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *
 		}
 	}
 
-	// Add initializer guard variable.
-	initguard := &Global{
-		Pkg:  p,
-		name: "init$guard",
-		typ:  types.NewPointer(tBool),
+	if prog.mode&BareInits == 0 {
+		// Add initializer guard variable.
+		initguard := &Global{
+			Pkg:  p,
+			name: "init$guard",
+			typ:  types.NewPointer(tBool),
+		}
+		p.Members[initguard.Name()] = initguard
 	}
-	p.Members[initguard.Name()] = initguard
 
 	if prog.mode&GlobalDebug != 0 {
 		p.SetDebugMode(true)
@@ -268,8 +276,8 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *
 // printMu serializes printing of Packages/Functions to stdout.
 var printMu sync.Mutex
 
-// AllPackages returns a new slice containing all packages in the
-// program prog in unspecified order.
+// AllPackages returns a new slice containing all packages created by
+// prog.CreatePackage in unspecified order.
 func (prog *Program) AllPackages() []*Package {
 	pkgs := make([]*Package, 0, len(prog.packages))
 	for _, pkg := range prog.packages {
@@ -291,10 +299,21 @@ func (prog *Program) AllPackages() []*Package {
 // false---yet this function remains very convenient.
 // Clients should use (*Program).Package instead where possible.
 // IR doesn't really need a string-keyed map of packages.
+//
+// Furthermore, the graph of packages may contain multiple variants
+// (e.g. "p" vs "p as compiled for q.test"), and each has a different
+// view of its dependencies.
 func (prog *Program) ImportedPackage(path string) *Package {
 	return prog.imported[path]
 }
 
+// SetNoReturn sets the predicate used when building the ir.Program
+// prog that reports whether a given function cannot return.
+// This may be used to prune spurious control flow edges
+// after (e.g.) log.Fatal, improving the precision of analyses.
+//
+// A typical implementation is the [ctrlflow.CFGs.NoReturn] method from
+// [golang.org/x/tools/go/analysis/passes/ctrlflow].
 func (prog *Program) SetNoReturn(fn func(*types.Func) bool) {
 	prog.noReturn = fn
 }
diff --git a/vendor/honnef.co/go/tools/go/ir/doc.go b/vendor/honnef.co/go/tools/go/ir/doc.go
index e15ef60f..13e47498 100644
--- a/vendor/honnef.co/go/tools/go/ir/doc.go
+++ b/vendor/honnef.co/go/tools/go/ir/doc.go
@@ -4,34 +4,29 @@
 
 // Package ir defines a representation of the elements of Go programs
 // (packages, types, functions, variables and constants) using a
-// static single-information (SSI) form intermediate representation
+// static single-assignment (SSA) form intermediate representation
 // (IR) for the bodies of functions.
 //
-// THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE.
-//
-// For an introduction to SSA form, upon which SSI builds, see
-// https://en.wikipedia.org/wiki/Static_single_assignment_form.
+// For an introduction to SSA form, see
+// http://en.wikipedia.org/wiki/Static_single_assignment_form.
 // This page provides a broader reading list:
-// https://www.dcs.gla.ac.uk/~jsinger/ssa.html.
-//
-// For an introduction to SSI form, see The static single information
-// form by C. Scott Ananian.
+// http://www.dcs.gla.ac.uk/~jsinger/ssa.html.
 //
 // The level of abstraction of the IR form is intentionally close to
 // the source language to facilitate construction of source analysis
 // tools.  It is not intended for machine code generation.
 //
 // The simplest way to create the IR of a package is
-// to load typed syntax trees using golang.org/x/tools/go/packages, then
-// invoke the irutil.Packages helper function. See ExampleLoadPackages
-// and ExampleWholeProgram for examples.
-// The resulting ir.Program contains all the packages and their
+// to load typed syntax trees using [golang.org/x/tools/go/packages], then
+// invoke the [honnef.co/go/tools/go/ir/irutil.Packages] helper function.
+// (See the package-level Examples named LoadPackages and LoadWholeProgram.)
+// The resulting [ir.Program] contains all the packages and their
 // members, but IR code is not created for function bodies until a
-// subsequent call to (*Package).Build or (*Program).Build.
+// subsequent call to [Package.Build] or [Program.Build].
 //
 // The builder initially builds a naive IR form in which all local
 // variables are addresses of stack locations with explicit loads and
-// stores.  Registerization of eligible locals and φ-node insertion
+// stores.  Registerisation of eligible locals and φ-node insertion
 // using dominance and dataflow are then performed as a second pass
 // called "lifting" to improve the accuracy and performance of
 // subsequent analyses; this pass can be skipped by setting the
@@ -39,16 +34,16 @@
 //
 // The primary interfaces of this package are:
 //
-//   - Member: a named member of a Go package.
-//   - Value: an expression that yields a value.
-//   - Instruction: a statement that consumes values and performs computation.
-//   - Node: a Value or Instruction (emphasizing its membership in the IR value graph)
+//   - [Member]: a named member of a Go package.
+//   - [Value]: an expression that yields a value.
+//   - [Instruction]: a statement that consumes values and performs computation.
+//   - [Node]: a [Value] or [Instruction] (emphasizing its membership in the IR value graph)
 //
-// A computation that yields a result implements both the Value and
-// Instruction interfaces.  The following table shows for each
+// A computation that yields a result implements both the [Value] and
+// [Instruction] interfaces.  The following table shows for each
 // concrete type which of these interfaces it implements.
 //
-//	                   Value?          Instruction?    Member?
+//	                   Value?          Instruction?      Member?
 //	*Alloc                ✔               ✔
 //	*BinOp                ✔               ✔
 //	*BlankStore                           ✔
@@ -59,14 +54,14 @@
 //	*Const                ✔               ✔
 //	*Convert              ✔               ✔
 //	*DebugRef                             ✔
-//	*Defer                ✔               ✔
+//	*Defer                                ✔
 //	*Extract              ✔               ✔
 //	*Field                ✔               ✔
 //	*FieldAddr            ✔               ✔
 //	*FreeVar              ✔
 //	*Function             ✔                               ✔ (func)
 //	*Global               ✔                               ✔ (var)
-//	*Go                   ✔               ✔
+//	*Go                                   ✔
 //	*If                                   ✔
 //	*Index                ✔               ✔
 //	*IndexAddr            ✔               ✔
@@ -78,7 +73,7 @@
 //	*MakeMap              ✔               ✔
 //	*MakeSlice            ✔               ✔
 //	*MapLookup            ✔               ✔
-//	*MapUpdate            ✔               ✔
+//	*MapUpdate                            ✔
 //	*MultiConvert         ✔               ✔
 //	*NamedConst                                           ✔ (const)
 //	*Next                 ✔               ✔
@@ -90,27 +85,26 @@
 //	*Return                               ✔
 //	*RunDefers                            ✔
 //	*Select               ✔               ✔
-//	*Send                 ✔               ✔
-//	*Sigma                ✔               ✔
+//	*Send                                 ✔
 //	*Slice                ✔               ✔
 //	*SliceToArrayPointer  ✔               ✔
 //	*SliceToArray         ✔               ✔
-//	*Store                ✔               ✔
+//	*Store                                ✔
 //	*StringLookup         ✔               ✔
 //	*Type                                                 ✔ (type)
 //	*TypeAssert           ✔               ✔
 //	*UnOp                 ✔               ✔
 //	*Unreachable                          ✔
 //
-// Other key types in this package include: Program, Package, Function
-// and BasicBlock.
+// Other key types in this package include: [Program], [Package], [Function]
+// and [BasicBlock].
 //
 // The program representation constructed by this package is fully
 // resolved internally, i.e. it does not rely on the names of Values,
 // Packages, Functions, Types or BasicBlocks for the correct
 // interpretation of the program.  Only the identities of objects and
 // the topology of the IR and type graphs are semantically
-// significant.  (There is one exception: Ids, used to identify field
+// significant.  (There is one exception: [types.Id] values, which identify field
 // and method names, contain strings.)  Avoidance of name-based
 // operations simplifies the implementation of subsequent passes and
 // can make them very efficient.  Many objects are nonetheless named
@@ -118,14 +112,12 @@
 // either accurate or unambiguous.  The public API exposes a number of
 // name-based maps for client convenience.
 //
-// The ir/irutil package provides various utilities that depend only
-// on the public API of this package.
-//
-// TODO(adonovan): Consider the exceptional control-flow implications
-// of defer and recover().
+// The [honnef.co/go/tools/go/ir/irutil] package provides various
+// helper functions, for example to simplify loading a Go program into
+// SSA form.
 //
 // TODO(adonovan): write a how-to document for all the various cases
 // of trying to determine corresponding elements across the four
 // domains of source locations, ast.Nodes, types.Objects,
-// ir.Values/Instructions.
-package ir
+// ssa.Values/Instructions.
+package ir // import "honnef.co/go/tools/go/ir"
diff --git a/vendor/honnef.co/go/tools/go/ir/dom.go b/vendor/honnef.co/go/tools/go/ir/dom.go
index f63a4c40..d733c950 100644
--- a/vendor/honnef.co/go/tools/go/ir/dom.go
+++ b/vendor/honnef.co/go/tools/go/ir/dom.go
@@ -23,12 +23,14 @@ import (
 	"io"
 	"math/big"
 	"os"
+	"slices"
 	"sort"
 )
 
 // Idom returns the block that immediately dominates b:
 // its parent in the dominator tree, if any.
-// The entry node (b.Index==0) does not have a parent.
+// Neither the entry node (b.Index==0) nor recover node
+// (b==b.Parent().Recover()) have a parent.
 func (b *BasicBlock) Idom() *BasicBlock { return b.dom.idom }
 
 // Dominees returns the list of blocks that b immediately dominates:
@@ -40,20 +42,25 @@ func (b *BasicBlock) Dominates(c *BasicBlock) bool {
 	return b.dom.pre <= c.dom.pre && c.dom.post <= b.dom.post
 }
 
-type byDomPreorder []*BasicBlock
-
-func (a byDomPreorder) Len() int           { return len(a) }
-func (a byDomPreorder) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a byDomPreorder) Less(i, j int) bool { return a[i].dom.pre < a[j].dom.pre }
-
-// DomPreorder returns a new slice containing the blocks of f in
-// dominator tree preorder.
+// DomPreorder returns a new slice containing the blocks of f
+// in a preorder traversal of the dominator tree.
 func (f *Function) DomPreorder() []*BasicBlock {
-	n := len(f.Blocks)
-	order := make(byDomPreorder, n)
-	copy(order, f.Blocks)
-	sort.Sort(order)
-	return order
+	slice := slices.Clone(f.Blocks)
+	sort.Slice(slice, func(i, j int) bool {
+		return slice[i].dom.pre < slice[j].dom.pre
+	})
+	return slice
+}
+
+// DomPostorder returns a new slice containing the blocks of f
+// in a postorder traversal of the dominator tree.
+// (This is not the same as a postdominance order.)
+func (f *Function) DomPostorder() []*BasicBlock {
+	slice := slices.Clone(f.Blocks)
+	sort.Slice(slice, func(i, j int) bool {
+		return slice[i].dom.post < slice[j].dom.post
+	})
+	return slice
 }
 
 // domInfo contains a BasicBlock's dominance information.
@@ -63,6 +70,48 @@ type domInfo struct {
 	pre, post int32         // pre- and post-order numbering within domtree
 }
 
+// ltState holds the working state for Lengauer-Tarjan algorithm
+// (during which domInfo.pre is repurposed for CFG DFS preorder number).
+type ltState struct {
+	// Each slice is indexed by b.Index.
+	sdom     []*BasicBlock // b's semidominator
+	parent   []*BasicBlock // b's parent in DFS traversal of CFG
+	ancestor []*BasicBlock // b's ancestor with least sdom
+}
+
+// dfs implements the depth-first search part of the LT algorithm.
+func (lt *ltState) dfs(v *BasicBlock, i int32, preorder []*BasicBlock) int32 {
+	preorder[i] = v
+	v.dom.pre = i // For now: DFS preorder of spanning tree of CFG
+	i++
+	lt.sdom[v.Index] = v
+	lt.link(nil, v)
+	for _, w := range v.Succs {
+		if lt.sdom[w.Index] == nil {
+			lt.parent[w.Index] = v
+			i = lt.dfs(w, i, preorder)
+		}
+	}
+	return i
+}
+
+// eval implements the EVAL part of the LT algorithm.
+func (lt *ltState) eval(v *BasicBlock) *BasicBlock {
+	// TODO(adonovan): opt: do path compression per simple LT.
+	u := v
+	for ; lt.ancestor[v.Index] != nil; v = lt.ancestor[v.Index] {
+		if lt.sdom[v.Index].dom.pre < lt.sdom[u.Index].dom.pre {
+			u = v
+		}
+	}
+	return u
+}
+
+// link implements the LINK part of the LT algorithm.
+func (lt *ltState) link(v, w *BasicBlock) {
+	lt.ancestor[w.Index] = v
+}
+
 // buildDomTree computes the dominator tree of f using the LT algorithm.
 // Precondition: all blocks are reachable (e.g. optimizeBlocks has been run).
 func buildDomTree(fn *Function) {
@@ -74,199 +123,90 @@ func buildDomTree(fn *Function) {
 		b.dom = domInfo{}
 	}
 
-	idoms := make([]*BasicBlock, len(fn.Blocks))
-
-	order := make([]*BasicBlock, 0, len(fn.Blocks))
-	seen := fn.blockset(0)
-	var dfs func(b *BasicBlock)
-	dfs = func(b *BasicBlock) {
-		if !seen.Add(b) {
-			return
-		}
-		for _, succ := range b.Succs {
-			dfs(succ)
-		}
-		if fn.fakeExits.Has(b) {
-			dfs(fn.Exit)
-		}
-		order = append(order, b)
-		b.post = len(order) - 1
+	n := len(fn.Blocks)
+	// Allocate space for 5 contiguous [n]*BasicBlock arrays:
+	// sdom, parent, ancestor, preorder, buckets.
+	space := make([]*BasicBlock, 5*n)
+	lt := ltState{
+		sdom:     space[0:n],
+		parent:   space[n : 2*n],
+		ancestor: space[2*n : 3*n],
 	}
-	dfs(fn.Blocks[0])
 
-	for i := 0; i < len(order)/2; i++ {
-		o := len(order) - i - 1
-		order[i], order[o] = order[o], order[i]
+	// Step 1.  Number vertices by depth-first preorder.
+	preorder := space[3*n : 4*n]
+	root := fn.Blocks[0]
+	prenum := lt.dfs(root, 0, preorder)
+	recover := fn.Recover
+	if recover != nil {
+		lt.dfs(recover, prenum, preorder)
 	}
 
-	idoms[fn.Blocks[0].Index] = fn.Blocks[0]
-	changed := true
-	for changed {
-		changed = false
-		// iterate over all nodes in reverse postorder, except for the
-		// entry node
-		for _, b := range order[1:] {
-			var newIdom *BasicBlock
-			do := func(p *BasicBlock) {
-				if idoms[p.Index] == nil {
-					return
-				}
-				if newIdom == nil {
-					newIdom = p
-				} else {
-					finger1 := p
-					finger2 := newIdom
-					for finger1 != finger2 {
-						for finger1.post < finger2.post {
-							finger1 = idoms[finger1.Index]
-						}
-						for finger2.post < finger1.post {
-							finger2 = idoms[finger2.Index]
-						}
-					}
-					newIdom = finger1
-				}
-			}
-			for _, p := range b.Preds {
-				do(p)
-			}
-			if b == fn.Exit {
-				for _, p := range fn.Blocks {
-					if fn.fakeExits.Has(p) {
-						do(p)
-					}
-				}
-			}
+	buckets := space[4*n : 5*n]
+	copy(buckets, preorder)
 
-			if idoms[b.Index] != newIdom {
-				idoms[b.Index] = newIdom
-				changed = true
+	// In reverse preorder...
+	for i := int32(n) - 1; i > 0; i-- {
+		w := preorder[i]
+
+		// Step 3. Implicitly define the immediate dominator of each node.
+		for v := buckets[i]; v != w; v = buckets[v.dom.pre] {
+			u := lt.eval(v)
+			if lt.sdom[u.Index].dom.pre < i {
+				v.dom.idom = u
+			} else {
+				v.dom.idom = w
 			}
 		}
-	}
 
-	for i, b := range idoms {
-		fn.Blocks[i].dom.idom = b
-		if b == nil {
-			// malformed CFG
-			continue
-		}
-		if i == b.Index {
-			continue
+		// Step 2. Compute the semidominators of all nodes.
+		lt.sdom[w.Index] = lt.parent[w.Index]
+		for _, v := range w.Preds {
+			u := lt.eval(v)
+			if lt.sdom[u.Index].dom.pre < lt.sdom[w.Index].dom.pre {
+				lt.sdom[w.Index] = lt.sdom[u.Index]
+			}
 		}
-		b.dom.children = append(b.dom.children, fn.Blocks[i])
-	}
-
-	numberDomTree(fn.Blocks[0], 0, 0)
-
-	// printDomTreeDot(os.Stderr, fn) // debugging
-	// printDomTreeText(os.Stderr, root, 0) // debugging
-
-	if fn.Prog.mode&SanityCheckFunctions != 0 {
-		sanityCheckDomTree(fn)
-	}
-}
 
-// buildPostDomTree is like buildDomTree, but builds the post-dominator tree instead.
-func buildPostDomTree(fn *Function) {
-	// The step numbers refer to the original LT paper; the
-	// reordering is due to Georgiadis.
-
-	// Clear any previous domInfo.
-	for _, b := range fn.Blocks {
-		b.pdom = domInfo{}
-	}
-
-	idoms := make([]*BasicBlock, len(fn.Blocks))
+		lt.link(lt.parent[w.Index], w)
 
-	order := make([]*BasicBlock, 0, len(fn.Blocks))
-	seen := fn.blockset(0)
-	var dfs func(b *BasicBlock)
-	dfs = func(b *BasicBlock) {
-		if !seen.Add(b) {
-			return
-		}
-		for _, pred := range b.Preds {
-			dfs(pred)
-		}
-		if b == fn.Exit {
-			for _, p := range fn.Blocks {
-				if fn.fakeExits.Has(p) {
-					dfs(p)
-				}
-			}
+		if lt.parent[w.Index] == lt.sdom[w.Index] {
+			w.dom.idom = lt.parent[w.Index]
+		} else {
+			buckets[i] = buckets[lt.sdom[w.Index].dom.pre]
+			buckets[lt.sdom[w.Index].dom.pre] = w
 		}
-		order = append(order, b)
-		b.post = len(order) - 1
 	}
-	dfs(fn.Exit)
 
-	for i := 0; i < len(order)/2; i++ {
-		o := len(order) - i - 1
-		order[i], order[o] = order[o], order[i]
+	// The final 'Step 3' is now outside the loop.
+	for v := buckets[0]; v != root; v = buckets[v.dom.pre] {
+		v.dom.idom = root
 	}
 
-	idoms[fn.Exit.Index] = fn.Exit
-	changed := true
-	for changed {
-		changed = false
-		// iterate over all nodes in reverse postorder, except for the
-		// exit node
-		for _, b := range order[1:] {
-			var newIdom *BasicBlock
-			do := func(p *BasicBlock) {
-				if idoms[p.Index] == nil {
-					return
-				}
-				if newIdom == nil {
-					newIdom = p
-				} else {
-					finger1 := p
-					finger2 := newIdom
-					for finger1 != finger2 {
-						for finger1.post < finger2.post {
-							finger1 = idoms[finger1.Index]
-						}
-						for finger2.post < finger1.post {
-							finger2 = idoms[finger2.Index]
-						}
-					}
-					newIdom = finger1
-				}
-			}
-			for _, p := range b.Succs {
-				do(p)
-			}
-			if fn.fakeExits.Has(b) {
-				do(fn.Exit)
-			}
-
-			if idoms[b.Index] != newIdom {
-				idoms[b.Index] = newIdom
-				changed = true
+	// Step 4. Explicitly define the immediate dominator of each
+	// node, in preorder.
+	for _, w := range preorder[1:] {
+		if w == root || w == recover {
+			w.dom.idom = nil
+		} else {
+			if w.dom.idom != lt.sdom[w.Index] {
+				w.dom.idom = w.dom.idom.dom.idom
 			}
+			// Calculate Children relation as inverse of Idom.
+			w.dom.idom.dom.children = append(w.dom.idom.dom.children, w)
 		}
 	}
 
-	for i, b := range idoms {
-		fn.Blocks[i].pdom.idom = b
-		if b == nil {
-			// malformed CFG
-			continue
-		}
-		if i == b.Index {
-			continue
-		}
-		b.pdom.children = append(b.pdom.children, fn.Blocks[i])
+	pre, post := numberDomTree(root, 0, 0)
+	if recover != nil {
+		numberDomTree(recover, pre, post)
 	}
 
-	numberPostDomTree(fn.Exit, 0, 0)
-
-	// printPostDomTreeDot(os.Stderr, fn) // debugging
-	// printPostDomTreeText(os.Stderr, fn.Exit, 0) // debugging
+	// printDomTreeDot(os.Stderr, f)        // debugging
+	// printDomTreeText(os.Stderr, root, 0) // debugging
 
-	if fn.Prog.mode&SanityCheckFunctions != 0 { // XXX
-		sanityCheckDomTree(fn) // XXX
+	if fn.Prog.mode&SanityCheckFunctions != 0 {
+		sanityCheckDomTree(fn)
 	}
 }
 
@@ -284,20 +224,6 @@ func numberDomTree(v *BasicBlock, pre, post int32) (int32, int32) {
 	return pre, post
 }
 
-// numberPostDomTree sets the pre- and post-order numbers of a depth-first
-// traversal of the post-dominator tree rooted at v.  These are used to
-// answer post-dominance queries in constant time.
-func numberPostDomTree(v *BasicBlock, pre, post int32) (int32, int32) {
-	v.pdom.pre = pre
-	pre++
-	for _, child := range v.pdom.children {
-		pre, post = numberPostDomTree(child, pre, post)
-	}
-	v.pdom.post = post
-	post++
-	return pre, post
-}
-
 // Testing utilities ----------------------------------------
 
 // sanityCheckDomTree checks the correctness of the dominator tree
@@ -318,8 +244,8 @@ func sanityCheckDomTree(f *Function) {
 	all.Set(one).Lsh(&all, uint(n)).Sub(&all, one)
 
 	// Initialization.
-	for i := range f.Blocks {
-		if i == 0 {
+	for i, b := range f.Blocks {
+		if i == 0 || b == f.Recover {
 			// A root is dominated only by itself.
 			D[i].SetBit(&D[0], 0, 1)
 		} else {
@@ -333,7 +259,7 @@ func sanityCheckDomTree(f *Function) {
 	for changed := true; changed; {
 		changed = false
 		for i, b := range f.Blocks {
-			if i == 0 {
+			if i == 0 || b == f.Recover {
 				continue
 			}
 			// Compute intersection across predecessors.
@@ -342,13 +268,6 @@ func sanityCheckDomTree(f *Function) {
 			for _, pred := range b.Preds {
 				x.And(&x, &D[pred.Index])
 			}
-			if b == f.Exit {
-				for _, p := range f.Blocks {
-					if f.fakeExits.Has(p) {
-						x.And(&x, &D[p.Index])
-					}
-				}
-			}
 			x.SetBit(&x, i, 1) // a block always dominates itself.
 			if D[i].Cmp(&x) != 0 {
 				D[i].Set(&x)
@@ -358,10 +277,14 @@ func sanityCheckDomTree(f *Function) {
 	}
 
 	// Check the entire relation.  O(n^2).
+	// The Recover block (if any) must be treated specially so we skip it.
 	ok := true
 	for i := range n {
 		for j := range n {
 			b, c := f.Blocks[i], f.Blocks[j]
+			if c == f.Recover {
+				continue
+			}
 			actual := b.Dominates(c)
 			expected := D[j].Bit(i) == 1
 			if actual != expected {
@@ -382,12 +305,11 @@ func sanityCheckDomTree(f *Function) {
 	if !ok {
 		panic("sanityCheckDomTree failed for " + f.String())
 	}
-
 }
 
 // Printing functions ----------------------------------------
 
-// printDomTree prints the dominator tree as text, using indentation.
+// printDomTreeText prints the dominator tree as text, using indentation.
 //
 //lint:ignore U1000 used during debugging
 func printDomTreeText(buf *bytes.Buffer, v *BasicBlock, indent int) {
@@ -418,49 +340,6 @@ func printDomTreeDot(buf io.Writer, f *Function) {
 		for _, pred := range b.Preds {
 			fmt.Fprintf(buf, "\tn%d -> n%d [style=\"dotted\",weight=0];\n", pred.dom.pre, v.pre)
 		}
-
-		if f.fakeExits.Has(b) {
-			fmt.Fprintf(buf, "\tn%d -> n%d [style=\"dotted\",weight=0,color=red];\n", b.dom.pre, f.Exit.dom.pre)
-		}
-	}
-	fmt.Fprintln(buf, "}")
-}
-
-// printDomTree prints the dominator tree as text, using indentation.
-//
-//lint:ignore U1000 used during debugging
-func printPostDomTreeText(buf io.Writer, v *BasicBlock, indent int) {
-	fmt.Fprintf(buf, "%*s%s\n", 4*indent, "", v)
-	for _, child := range v.pdom.children {
-		printPostDomTreeText(buf, child, indent+1)
-	}
-}
-
-// printDomTreeDot prints the dominator tree of f in AT&T GraphViz
-// (.dot) format.
-//
-//lint:ignore U1000 used during debugging
-func printPostDomTreeDot(buf io.Writer, f *Function) {
-	fmt.Fprintln(buf, "//", f)
-	fmt.Fprintln(buf, "digraph pdomtree {")
-	for _, b := range f.Blocks {
-		v := b.pdom
-		fmt.Fprintf(buf, "\tn%d [label=\"%s (%d, %d)\",shape=\"rectangle\"];\n", v.pre, b, v.pre, v.post)
-		// TODO(adonovan): improve appearance of edges
-		// belonging to both dominator tree and CFG.
-
-		// Dominator tree edge.
-		if b != f.Exit {
-			fmt.Fprintf(buf, "\tn%d -> n%d [style=\"solid\",weight=100];\n", v.idom.pdom.pre, v.pre)
-		}
-		// CFG edges.
-		for _, pred := range b.Preds {
-			fmt.Fprintf(buf, "\tn%d -> n%d [style=\"dotted\",weight=0];\n", pred.pdom.pre, v.pre)
-		}
-
-		if f.fakeExits.Has(b) {
-			fmt.Fprintf(buf, "\tn%d -> n%d [style=\"dotted\",weight=0,color=red];\n", b.dom.pre, f.Exit.dom.pre)
-		}
 	}
 	fmt.Fprintln(buf, "}")
 }
diff --git a/vendor/honnef.co/go/tools/go/ir/emit.go b/vendor/honnef.co/go/tools/go/ir/emit.go
index 4eecf24d..bd9a5bc3 100644
--- a/vendor/honnef.co/go/tools/go/ir/emit.go
+++ b/vendor/honnef.co/go/tools/go/ir/emit.go
@@ -13,8 +13,6 @@ import (
 	"go/token"
 	"go/types"
 
-	"honnef.co/go/tools/go/types/typeutil"
-
 	"golang.org/x/exp/typeparams"
 )
 
@@ -49,7 +47,7 @@ func emitNew(f *Function, typ types.Type, source ast.Node, comment string) *Allo
 // emits an Alloc instruction for it.
 //
 // (Use this function or emitNew for synthetic variables;
-// for source-level variables, use emitLocalVar.)
+// for source-level variables in the same function, use emitLocalVar.)
 func emitLocal(f *Function, t types.Type, source ast.Node, comment string) *Alloc {
 	local := emitAlloc(f, t, source, comment)
 	f.Locals = append(f.Locals, local)
@@ -58,8 +56,9 @@ func emitLocal(f *Function, t types.Type, source ast.Node, comment string) *Allo
 
 // emitLocalVar creates a local var for v and emits an Alloc instruction for it.
 // Subsequent calls to f.lookup(v) return it.
+// It applies the appropriate generic instantiation to the type.
 func emitLocalVar(f *Function, v *types.Var, source ast.Node) *Alloc {
-	alloc := emitLocal(f, v.Type(), source, v.Name())
+	alloc := emitLocal(f, f.typ(v.Type()), source, v.Name())
 	f.vars[v] = alloc
 	return alloc
 }
@@ -92,7 +91,7 @@ func emitDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) {
 	f.emit(ref, nil)
 }
 
-func makeDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) *DebugRef {
+func makeDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) *debugRef {
 	if !f.debugInfo() {
 		return nil // debugging not enabled
 	}
@@ -100,18 +99,18 @@ func makeDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) *DebugRef {
 		panic("nil")
 	}
 	var obj types.Object
-	e = unparen(e)
+	e = ast.Unparen(e)
 	if id, ok := e.(*ast.Ident); ok {
 		if isBlankIdent(id) {
 			return nil
 		}
-		obj = f.Pkg.objectOf(id)
+		obj = f.objectOf(id)
 		switch obj.(type) {
 		case *types.Nil, *types.Const, *types.Builtin:
 			return nil
 		}
 	}
-	return &DebugRef{
+	return &debugRef{
 		X:      v,
 		Expr:   e,
 		IsAddr: isAddr,
@@ -200,7 +199,7 @@ func emitCompare(f *Function, op token.Token, x, y Value, source ast.Node) Value
 
 // isValuePreserving returns true if a conversion from ut_src to
 // ut_dst is value-preserving, i.e. just a change of type.
-// Precondition: neither argument is a named type.
+// Precondition: neither argument is a named or alias type.
 func isValuePreserving(ut_src, ut_dst types.Type) bool {
 	// Identical underlying types?
 	if types.IdenticalIgnoreTags(ut_dst, ut_src) {
@@ -254,7 +253,7 @@ func emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value {
 
 		// Untyped nil constant?  Return interface-typed nil constant.
 		if ut_src == tUntypedNil {
-			return emitConst(f, zeroConst(t_dst, source))
+			return zeroConst(t_dst, source)
 		}
 
 		// Convert (non-nil) "untyped" literals to their default type.
@@ -262,20 +261,19 @@ func emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value {
 			val = emitConv(f, val, types.Default(ut_src), source)
 		}
 
-		f.Pkg.Prog.needMethodsOf(val.Type())
+		// Record the types of operands to MakeInterface, if
+		// non-parameterized, as they are the set of runtime types.
+		t := val.Type()
+		if !f.Prog.isParameterized(t) {
+			addMakeInterfaceType(f.Prog, t)
+		}
+
 		mi := &MakeInterface{X: val}
 		mi.setType(t_dst)
 		return f.emit(mi, source)
 	}
 
-	// In the common case, the typesets of src and dst are singletons
-	// and we emit an appropriate conversion. But if either contains
-	// a type parameter, the conversion may represent a cross product,
-	// in which case which we emit a MultiConvert.
-	tset_dst := typeutil.NewTypeSet(ut_dst)
-	tset_src := typeutil.NewTypeSet(ut_src)
-
-	// conversionCase describes an instruction pattern that may be emitted to
+	// conversionCase describes an instruction pattern that maybe emitted to
 	// model d <- s for d in dst_terms and s in src_terms.
 	// Multiple conversions can match the same pattern.
 	type conversionCase uint8
@@ -287,7 +285,8 @@ func emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value {
 		sliceTo0ArrayPtr
 		convert
 	)
-
+	// classify the conversion case of a source type us to a destination type ud.
+	// us and ud are underlying types (not *Named or *Alias)
 	classify := func(s, d types.Type) conversionCase {
 		// Just a change of type, but not value or representation?
 		if isValuePreserving(s, d) {
@@ -331,13 +330,14 @@ func emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value {
 	}
 
 	var classifications conversionCase
-	for _, s := range tset_src.Terms {
-		us := s.Type().Underlying()
-		for _, d := range tset_dst.Terms {
-			ud := d.Type().Underlying()
-			classifications |= classify(us, ud)
-		}
-	}
+	underIs(ut_src, func(us types.Type) bool {
+		return underIs(ut_dst, func(ud types.Type) bool {
+			if us != nil && ud != nil {
+				classifications |= classify(us, ud)
+			}
+			return classifications != 0
+		})
+	})
 	if classifications == 0 {
 		panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), t_dst))
 	}
@@ -351,12 +351,12 @@ func emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value {
 			// constant of the destination type and
 			// (initially) the same abstract value.
 			// We don't truncate the value yet.
-			return emitConst(f, NewConst(c.Value, t_dst, source))
+			return NewConst(c.Value, t_dst, source)
 		}
 		// Can we always convert from zero value without panicking?
 		const mayPanic = sliceToArray | sliceToArrayPtr
 		if c.Value == nil && classifications&mayPanic == 0 {
-			return emitConst(f, NewConst(nil, t_dst, source))
+			return NewConst(nil, t_dst, source)
 		}
 
 		// We're converting from constant to non-constant type,
@@ -380,26 +380,54 @@ func emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value {
 		return f.emit(p, source)
 
 	case sliceTo0Array: // slice to zero-length arrays (constant)
-		return emitConst(f, zeroConst(t_dst, source))
+		return zeroConst(t_dst, source)
 
 	case convert: // representation-changing conversion
 		c := &Convert{X: val}
 		c.setType(t_dst)
 		return f.emit(c, source)
 
-	default: // multiple conversion
-		c := &MultiConvert{X: val, from: tset_src, to: tset_dst}
+	default: // The conversion represents a cross product.
+		c := &MultiConvert{X: val, from: t_src, to: t_dst}
 		c.setType(t_dst)
 		return f.emit(c, source)
 	}
 }
 
+// emitTypeCoercion emits to f code to coerce the type of a
+// Value v to exactly type typ, and returns the coerced value.
+//
+// Requires that coercing v.Typ() to typ is a value preserving change.
+//
+// Currently used only when v.Type() is a type instance of typ or vice versa.
+// A type v is a type instance of a type t if there exists a
+// type parameter substitution σ s.t. σ(v) == t. Example:
+//
+//	σ(func(T) T) == func(int) int for σ == [T ↦ int]
+//
+// This happens in instantiation wrappers for conversion
+// from an instantiation to a parameterized type (and vice versa)
+// with σ substituting f.typeparams by f.typeargs.
+func emitTypeCoercion(f *Function, v Value, typ types.Type, source ast.Node) Value {
+	if types.Identical(v.Type(), typ) {
+		return v // no coercion needed
+	}
+	// TODO(taking): for instances should we record which side is the instance?
+	c := &ChangeType{
+		X: v,
+	}
+	c.setType(typ)
+	f.emit(c, source)
+	return c
+}
+
 // emitStore emits to f an instruction to store value val at location
 // addr, applying implicit conversions as required by assignability rules.
 func emitStore(f *Function, addr, val Value, source ast.Node) *Store {
+	typ := deref(addr.Type())
 	s := &Store{
 		Addr: addr,
-		Val:  emitConv(f, val, deref(addr.Type()), source),
+		Val:  emitConv(f, val, typ, source),
 	}
 	f.emit(s, source)
 	return s
@@ -473,7 +501,7 @@ func emitTailCall(f *Function, call *Call, source ast.Node) {
 	} else {
 		call.typ = tresults
 	}
-	tuple := f.emit(call, source)
+	tuple := emitCall(f, call, source)
 	var ret Return
 	switch nr {
 	case 0:
@@ -491,13 +519,12 @@ func emitTailCall(f *Function, call *Call, source ast.Node) {
 		}
 	}
 
-	f.Exit = f.newBasicBlock("exit")
-	emitJump(f, f.Exit, source)
-	f.currentBlock = f.Exit
 	f.emit(&ret, source)
 	f.currentBlock = nil
 }
 
+// emitCall emits a call instruction. If the callee is "no return",
+// it also emits a panic to eliminate infeasible CFG edges.
 func emitCall(fn *Function, call *Call, source ast.Node) Value {
 	res := fn.emit(call, source)
 
@@ -508,9 +535,10 @@ func emitCall(fn *Function, call *Call, source ast.Node) Value {
 		fn.Prog.noReturn(callee.object) {
 		// Call doesn't return normally. Either it doesn't return at all
 		// (infinitely blocked or exitting the process), or it unwinds the stack
-		// (panic, runtime.Goexit). In case it unwinds, jump to the exit block.
-		fn.emit(new(Jump), source)
-		addEdge(fn.currentBlock, fn.Exit)
+		// (panic, runtime.Goexit). Model this as a panic.
+		fn.emit(&Panic{
+			X: emitConv(fn, vNoReturn, tEface, source),
+		}, source)
 		fn.currentBlock = fn.newBasicBlock("unreachable")
 	}
 
@@ -529,9 +557,8 @@ func emitImplicitSelections(f *Function, v Value, indices []int, source ast.Node
 		// We may have a generic type containing a pointer, or a pointer to a generic type containing a struct. A
 		// pointer to a generic containing a pointer to a struct shouldn't be possible because the outer pointer gets
 		// dereferenced implicitly before we get here.
-		fld := typeutil.CoreType(deref(v.Type())).Underlying().(*types.Struct).Field(index)
-
-		if isPointer(v.Type()) {
+		if isPointerCore(v.Type()) {
+			fld := fieldOf(deref(v.Type()), index)
 			instr := &FieldAddr{
 				X:     v,
 				Field: index,
@@ -539,10 +566,11 @@ func emitImplicitSelections(f *Function, v Value, indices []int, source ast.Node
 			instr.setType(types.NewPointer(fld.Type()))
 			v = f.emit(instr, source)
 			// Load the field's value iff indirectly embedded.
-			if isPointer(fld.Type()) {
+			if isPointerCore(fld.Type()) {
 				v = emitLoad(f, v, source)
 			}
 		} else {
+			fld := fieldOf(v.Type(), index)
 			instr := &Field{
 				X:     v,
 				Field: index,
@@ -561,12 +589,8 @@ func emitImplicitSelections(f *Function, v Value, indices []int, source ast.Node
 // 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 {
-	// We may have a generic type containing a pointer, or a pointer to a generic type containing a struct. A
-	// pointer to a generic containing a pointer to a struct shouldn't be possible because the outer pointer gets
-	// dereferenced implicitly before we get here.
-	vut := typeutil.CoreType(deref(v.Type())).Underlying().(*types.Struct)
-	fld := vut.Field(index)
-	if isPointer(v.Type()) {
+	if isPointerCore(v.Type()) {
+		fld := fieldOf(deref(v.Type()), index)
 		instr := &FieldAddr{
 			X:     v,
 			Field: index,
@@ -579,6 +603,7 @@ func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.
 			v = emitLoad(f, v, id)
 		}
 	} else {
+		fld := fieldOf(v.Type(), index)
 		instr := &Field{
 			X:     v,
 			Field: index,
@@ -591,68 +616,30 @@ func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.
 	return v
 }
 
-// zeroValue emits to f code to produce a zero value of type t,
-// and returns it.
-func zeroValue(f *Function, t types.Type, source ast.Node) Value {
-	return emitConst(f, zeroConst(t, source))
-}
-
-type constKey struct {
-	typ    types.Type
-	value  constant.Value
-	source ast.Node
-}
-
-func emitConst(f *Function, c Constant) Constant {
-	if f.consts == nil {
-		f.consts = map[constKey]constValue{}
+// createRecoverBlock emits to f a block of code to return after a
+// recovered panic, and sets f.Recover to it.
+//
+// If f's result parameters are named, the code loads and returns
+// their current values, otherwise it returns the zero values of their
+// type.
+//
+// Idempotent.
+func createRecoverBlock(f *Function) {
+	if f.Recover != nil {
+		return // already created
 	}
+	saved := f.currentBlock
 
-	typ := c.Type()
-	var val constant.Value
-	switch c := c.(type) {
-	case *Const:
-		val = c.Value
-	case *ArrayConst, *GenericConst:
-		// These can only represent zero values, so all we need is the type
-	case *AggregateConst:
-		candidates, _ := f.aggregateConsts.At(c.typ)
-		for _, candidate := range candidates {
-			if c.equal(candidate) {
-				return candidate
-			}
-		}
+	f.Recover = f.newBasicBlock("recover")
+	f.currentBlock = f.Recover
 
-		for i := range c.Values {
-			c.Values[i] = emitConst(f, c.Values[i].(Constant))
-		}
+	var results []Value
+	// Reload NRPs to form value tuple.
+	for _, nr := range f.results {
+		results = append(results, emitLoad(f, nr, nr.source))
+	}
 
-		c.setBlock(f.Blocks[0])
-		rands := c.Operands(nil)
-		updateOperandsReferrers(c, rands)
-		candidates = append(candidates, c)
-		f.aggregateConsts.Set(c.typ, candidates)
-		return c
+	f.emit(&Return{Results: results}, nil)
 
-	default:
-		panic(fmt.Sprintf("unexpected type %T", c))
-	}
-	k := constKey{
-		typ:    typ,
-		value:  val,
-		source: c.Source(),
-	}
-	dup, ok := f.consts[k]
-	if ok {
-		return dup.c
-	} else {
-		c.setBlock(f.Blocks[0])
-		f.consts[k] = constValue{
-			c:   c,
-			idx: len(f.consts),
-		}
-		rands := c.Operands(nil)
-		updateOperandsReferrers(c, rands)
-		return c
-	}
+	f.currentBlock = saved
 }
diff --git a/vendor/honnef.co/go/tools/go/ir/func.go b/vendor/honnef.co/go/tools/go/ir/func.go
index 923b0496..5b0c338e 100644
--- a/vendor/honnef.co/go/tools/go/ir/func.go
+++ b/vendor/honnef.co/go/tools/go/ir/func.go
@@ -4,163 +4,99 @@
 
 package ir
 
-// This file implements the Function and BasicBlock types.
+// This file implements the Function type.
 
 import (
 	"bytes"
 	"fmt"
 	"go/ast"
-	"go/format"
 	"go/token"
 	"go/types"
 	"io"
+	"iter"
 	"os"
-	"sort"
 	"strings"
-
-	"honnef.co/go/tools/go/types/typeutil"
 )
 
-// addEdge adds a control-flow graph edge from from to to.
-func addEdge(from, to *BasicBlock) {
-	from.Succs = append(from.Succs, to)
-	to.Preds = append(to.Preds, from)
-}
-
-// Control returns the last instruction in the block.
-func (b *BasicBlock) Control() Instruction {
-	if len(b.Instrs) == 0 {
-		return nil
+// Like ObjectOf, but panics instead of returning nil.
+// Only valid during f's create and build phases.
+func (f *Function) objectOf(id *ast.Ident) types.Object {
+	if o := f.info.ObjectOf(id); o != nil {
+		return o
 	}
-	return b.Instrs[len(b.Instrs)-1]
-}
-
-// SigmaFor returns the sigma node for v coming from pred.
-func (b *BasicBlock) SigmaFor(v Value, pred *BasicBlock) *Sigma {
-	for _, instr := range b.Instrs {
-		sigma, ok := instr.(*Sigma)
-		if !ok {
-			// no more sigmas
-			return nil
-		}
-		if sigma.From == pred && sigma.X == v {
-			return sigma
-		}
-	}
-	return nil
-}
-
-// Parent returns the function that contains block b.
-func (b *BasicBlock) Parent() *Function { return b.parent }
-
-// String returns a human-readable label of this block.
-// It is not guaranteed unique within the function.
-func (b *BasicBlock) String() string {
-	return fmt.Sprintf("%d", b.Index)
-}
-
-// emit appends an instruction to the current basic block.
-// If the instruction defines a Value, it is returned.
-func (b *BasicBlock) emit(i Instruction, source ast.Node) Value {
-	i.setSource(source)
-	i.setBlock(b)
-	b.Instrs = append(b.Instrs, i)
-	v, _ := i.(Value)
-	return v
+	panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s",
+		id.Name, f.Prog.Fset.Position(id.Pos())))
 }
 
-// predIndex returns the i such that b.Preds[i] == c or panics if
-// there is none.
-func (b *BasicBlock) predIndex(c *BasicBlock) int {
-	for i, pred := range b.Preds {
-		if pred == c {
-			return i
+// instrs returns an iterator that returns each reachable instruction of the SSA function.
+func (f *Function) instrs() iter.Seq[Instruction] {
+	return func(yield func(i Instruction) bool) {
+		for _, block := range f.Blocks {
+			for _, instr := range block.Instrs {
+				if !yield(instr) {
+					return
+				}
+			}
 		}
 	}
-	panic(fmt.Sprintf("no edge %s -> %s", c, b))
 }
 
-// succIndex returns the i such that b.Succs[i] == c or -1 if there is none.
-func (b *BasicBlock) succIndex(c *BasicBlock) int {
-	for i, succ := range b.Succs {
-		if succ == c {
-			return i
-		}
+// Like TypeOf, but panics instead of returning nil.
+// Only valid during f's create and build phases.
+func (f *Function) typeOf(e ast.Expr) types.Type {
+	if T := f.info.TypeOf(e); T != nil {
+		return f.typ(T)
 	}
-	return -1
+	panic(fmt.Sprintf("no type for %T @ %s", e, f.Prog.Fset.Position(e.Pos())))
 }
 
-// hasPhi returns true if b.Instrs contains φ-nodes.
-func (b *BasicBlock) hasPhi() bool {
-	_, ok := b.Instrs[0].(*Phi)
-	return ok
+// typ is the locally instantiated type of T.
+// If f is not an instantiation, then f.typ(T)==T.
+func (f *Function) typ(T types.Type) types.Type {
+	return f.subst.typ(T)
 }
 
-func (b *BasicBlock) Phis() []Instruction {
-	return b.phis()
-}
-
-// phis returns the prefix of b.Instrs containing all the block's φ-nodes.
-func (b *BasicBlock) phis() []Instruction {
-	for i, instr := range b.Instrs {
-		if _, ok := instr.(*Phi); !ok {
-			return b.Instrs[:i]
-		}
+// If id is an Instance, returns info.Instances[id].Type.
+// Otherwise returns f.typeOf(id).
+func (f *Function) instanceType(id *ast.Ident) types.Type {
+	if t, ok := f.info.Instances[id]; ok {
+		return t.Type
 	}
-	return nil // unreachable in well-formed blocks
+	return f.typeOf(id)
 }
 
-// replacePred replaces all occurrences of p in b's predecessor list with q.
-// Ordinarily there should be at most one.
-func (b *BasicBlock) replacePred(p, q *BasicBlock) {
-	for i, pred := range b.Preds {
-		if pred == p {
-			b.Preds[i] = q
-		}
-	}
-}
-
-// replaceSucc replaces all occurrences of p in b's successor list with q.
-// Ordinarily there should be at most one.
-func (b *BasicBlock) replaceSucc(p, q *BasicBlock) {
-	for i, succ := range b.Succs {
-		if succ == p {
-			b.Succs[i] = q
-		}
+// selection returns a *selection corresponding to f.info.Selections[selector]
+// with potential updates for type substitution.
+func (f *Function) selection(selector *ast.SelectorExpr) *selection {
+	sel := f.info.Selections[selector]
+	if sel == nil {
+		return nil
 	}
-}
 
-// removePred removes all occurrences of p in b's
-// predecessor list and φ-nodes.
-// Ordinarily there should be at most one.
-func (b *BasicBlock) removePred(p *BasicBlock) {
-	phis := b.phis()
+	switch sel.Kind() {
+	case types.MethodExpr, types.MethodVal:
+		if recv := f.typ(sel.Recv()); recv != sel.Recv() {
+			// recv changed during type substitution.
+			pkg := f.declaredPackage().Pkg
+			obj, index, indirect := types.LookupFieldOrMethod(recv, true, pkg, sel.Obj().Name())
 
-	// We must preserve edge order for φ-nodes.
-	j := 0
-	for i, pred := range b.Preds {
-		if pred != p {
-			b.Preds[j] = b.Preds[i]
-			// Strike out φ-edge too.
-			for _, instr := range phis {
-				phi := instr.(*Phi)
-				phi.Edges[j] = phi.Edges[i]
+			// sig replaces sel.Type(). See (types.Selection).Typ() for details.
+			sig := obj.Type().(*types.Signature)
+			sig = changeRecv(sig, newVar(sig.Recv().Name(), recv))
+			if sel.Kind() == types.MethodExpr {
+				sig = recvAsFirstArg(sig)
+			}
+			return &selection{
+				kind:     sel.Kind(),
+				recv:     recv,
+				typ:      sig,
+				obj:      obj,
+				index:    index,
+				indirect: indirect,
 			}
-			j++
-		}
-	}
-	// Nil out b.Preds[j:] and φ-edges[j:] to aid GC.
-	for i := j; i < len(b.Preds); i++ {
-		b.Preds[i] = nil
-		for _, instr := range phis {
-			instr.(*Phi).Edges[i] = nil
 		}
 	}
-	b.Preds = b.Preds[:j]
-	for _, instr := range phis {
-		phi := instr.(*Phi)
-		phi.Edges = phi.Edges[:j]
-	}
+	return toSelection(sel)
 }
 
 // Destinations associated with unlabelled for/switch/select stmts.
@@ -190,7 +126,7 @@ type lblock struct {
 //
 // label should be a non-blank identifier (label.Name != "_").
 func (f *Function) label(label *ast.Ident) *types.Label {
-	return f.Pkg.objectOf(label).(*types.Label)
+	return f.objectOf(label).(*types.Label)
 }
 
 // lblockOf returns the branch target associated with the
@@ -274,24 +210,16 @@ func (f *Function) addResultVar(v *types.Var, source ast.Node) {
 	f.returnVars = append(f.returnVars, v)
 }
 
+// addParamVar adds a parameter to f.Params.
 func (f *Function) addParamVar(v *types.Var, source ast.Node) *Parameter {
 	name := v.Name()
 	if name == "" {
 		name = fmt.Sprintf("arg%d", len(f.Params))
 	}
-	var b *BasicBlock
-	if len(f.Blocks) > 0 {
-		b = f.Blocks[0]
-	}
-	param := &Parameter{name: name}
-	param.setBlock(b)
-	param.setType(v.Type())
+	param := &Parameter{name: name, typ: f.typ(v.Type()), parent: f}
 	param.setSource(source)
 	param.object = v
 	f.Params = append(f.Params, param)
-	if b != nil {
-		f.Blocks[0].Instrs = append(f.Blocks[0].Instrs, param)
-	}
 	return param
 }
 
@@ -302,7 +230,6 @@ func (f *Function) addSpilledParam(obj *types.Var, source ast.Node) {
 	param := f.addParamVar(obj, source)
 	spill := emitLocalVar(f, obj, source)
 	emitStore(f, spill, param, source)
-	// f.emit(&Store{Addr: spill, Val: param})
 }
 
 // startBody initializes the function prior to generating IR code for its body.
@@ -328,30 +255,12 @@ func (f *Function) blockset(i int) *BlockSet {
 	return bs
 }
 
-func (f *Function) exitBlock() {
-	old := f.currentBlock
-
-	f.Exit = f.newBasicBlock("exit")
-	f.currentBlock = f.Exit
-
-	results := make([]Value, len(f.results))
-	// Run function calls deferred in this
-	// function when explicitly returning from it.
-	f.emit(new(RunDefers), nil)
-	for i, r := range f.results {
-		results[i] = emitLoad(f, r, nil)
-	}
-
-	f.emit(&Return{Results: results}, nil)
-	f.currentBlock = old
-}
-
 // createSyntacticParams populates f.Params and generates code (spills
 // and named result locals) for all the parameters declared in the
 // syntax.  In addition it populates the f.objects mapping.
 //
 // Preconditions:
-// f.startBody() was called.
+// f.startBody() was called. f.info != nil.
 // Postcondition:
 // len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
 func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
@@ -459,120 +368,18 @@ func buildReferrers(f *Function) {
 			updateOperandsReferrers(instr, rands)
 		}
 	}
-
-	for _, c := range f.consts {
-		rands = c.c.Operands(rands[:0])
-		updateOperandsReferrers(c.c, rands)
-	}
-}
-
-func (f *Function) emitConsts() {
-	defer func() {
-		f.consts = nil
-		f.aggregateConsts = typeutil.Map[[]*AggregateConst]{}
-	}()
-
-	if len(f.Blocks) == 0 {
-		return
-	}
-
-	// TODO(dh): our deduplication only works on booleans and
-	// integers. other constants are represented as pointers to
-	// things.
-	head := make([]constValue, 0, len(f.consts))
-	for _, c := range f.consts {
-		if len(*c.c.Referrers()) == 0 {
-			// TODO(dh): killing a const may make other consts dead, too
-			killInstruction(c.c)
-		} else {
-			head = append(head, c)
-		}
-	}
-	sort.Slice(head, func(i, j int) bool {
-		return head[i].idx < head[j].idx
-	})
-	entry := f.Blocks[0]
-	instrs := make([]Instruction, 0, len(entry.Instrs)+len(head))
-	for _, c := range head {
-		instrs = append(instrs, c.c)
-	}
-	f.aggregateConsts.Iterate(func(key types.Type, value []*AggregateConst) {
-		for _, c := range value {
-			instrs = append(instrs, c)
-		}
-	})
-
-	instrs = append(instrs, entry.Instrs...)
-	entry.Instrs = instrs
-}
-
-// buildFakeExits ensures that every block in the function is
-// reachable in reverse from the Exit block. This is required to build
-// a full post-dominator tree, and to ensure the exit block's
-// inclusion in the dominator tree.
-func buildFakeExits(fn *Function) {
-	// Find back-edges via forward DFS
-	fn.fakeExits = BlockSet{values: make([]bool, len(fn.Blocks))}
-	seen := fn.blockset(0)
-	backEdges := fn.blockset(1)
-
-	var dfs func(b *BasicBlock)
-	dfs = func(b *BasicBlock) {
-		if !seen.Add(b) {
-			backEdges.Add(b)
-			return
-		}
-		for _, pred := range b.Succs {
-			dfs(pred)
-		}
-	}
-	dfs(fn.Blocks[0])
-buildLoop:
-	for {
-		seen := fn.blockset(2)
-		var dfs func(b *BasicBlock)
-		dfs = func(b *BasicBlock) {
-			if !seen.Add(b) {
-				return
-			}
-			for _, pred := range b.Preds {
-				dfs(pred)
-			}
-			if b == fn.Exit {
-				for _, b := range fn.Blocks {
-					if fn.fakeExits.Has(b) {
-						dfs(b)
-					}
-				}
-			}
-		}
-		dfs(fn.Exit)
-
-		for _, b := range fn.Blocks {
-			if !seen.Has(b) && backEdges.Has(b) {
-				// Block b is not reachable from the exit block. Add a
-				// fake jump from b to exit, then try again. Note that we
-				// only add one fake edge at a time, as it may make
-				// multiple blocks reachable.
-				//
-				// We only consider those blocks that have back edges.
-				// Any unreachable block that doesn't have a back edge
-				// must flow into a loop, which by definition has a
-				// back edge. Thus, by looking for loops, we should
-				// need fewer fake edges overall.
-				fn.fakeExits.Add(b)
-				continue buildLoop
-			}
-		}
-
-		break
-	}
 }
 
-// finishBody() finalizes the function after IR code generation of its body.
+// finishBody() finalizes the contents of the function after IR code generation of its body.
+//
+// The function is not done being built until done() is called.
 func (f *Function) finishBody() {
 	f.currentBlock = nil
 	f.lblocks = nil
+	f.returnVars = nil
+	f.jump = nil
+	f.source = nil
+	f.exits = nil
 
 	// Remove from f.Locals any Allocs that escape to the heap.
 	j := 0
@@ -589,10 +396,9 @@ func (f *Function) finishBody() {
 	f.Locals = f.Locals[:j]
 
 	optimizeBlocks(f)
-	buildFakeExits(f)
 	buildReferrers(f)
 	buildDomTree(f)
-	buildPostDomTree(f)
+	buildSCCs(f)
 
 	if f.Prog.mode&NaiveForm == 0 {
 		for lift(f) {
@@ -603,39 +409,82 @@ func (f *Function) finishBody() {
 		}
 	}
 
-	// emit constants after lifting, because lifting may produce new constants, but before other variable splitting,
-	// because it expects constants to have been deduplicated.
-	f.emitConsts()
-
-	if f.Prog.mode&SplitAfterNewInformation != 0 {
-		splitOnNewInformation(f.Blocks[0], &StackMap{})
+	if f.debugInfo() {
+		// Turn debug references into a map
+		f.exprToValue = map[ast.Expr]struct {
+			v      Value
+			isAddr bool
+		}{}
+		for _, b := range f.Blocks {
+			newInstrs := b.Instrs[:0]
+			for _, instr := range b.Instrs {
+				if instr, ok := instr.(*debugRef); ok {
+					if refs := instr.X.Referrers(); refs != nil {
+						*refs = removeInstr(*refs, instr)
+					}
+					if _, ok := f.exprToValue[instr.Expr]; !ok {
+						f.exprToValue[instr.Expr] = struct {
+							v      Value
+							isAddr bool
+						}{instr.X, instr.IsAddr}
+					}
+					continue
+				}
+				newInstrs = append(newInstrs, instr)
+			}
+			b.Instrs = newInstrs
+		}
 	}
 
 	// clear remaining builder state
 	f.results = nil    // (used by lifting)
 	f.deferstack = nil // (used by lifting)
 	f.vars = nil       // (used by lifting)
-	f.goversion = ""
 
 	numberNodes(f)
 
-	defer f.wr.Close()
-	f.wr.WriteFunc("start", "start", f)
+	// clear out other function state (keep consistent with buildParamsOnly)
+	f.subst = nil
 
-	if f.Prog.mode&PrintFunctions != 0 {
-		printMu.Lock()
-		f.WriteTo(os.Stdout)
-		printMu.Unlock()
-	}
+	numberNodes(f)
+}
+
+// done marks the building of f's SSA body complete,
+// along with any nested functions, and optionally prints them.
+func (f *Function) done() {
+	assert(f.parent == nil, "done called on an anonymous function")
 
-	if f.Prog.mode&SanityCheckFunctions != 0 {
-		mustSanityCheck(f, nil)
+	var visit func(*Function)
+	visit = func(f *Function) {
+		for _, anon := range f.AnonFuncs {
+			visit(anon) // anon is done building before f.
+		}
+
+		f.uniq = 0    // done with uniq
+		f.build = nil // function is built
+
+		if f.Prog.mode&PrintFunctions != 0 {
+			printMu.Lock()
+			f.WriteTo(os.Stdout)
+			printMu.Unlock()
+		}
+
+		if f.Prog.mode&SanityCheckFunctions != 0 {
+			mustSanityCheck(f, nil)
+		}
 	}
+	visit(f)
 }
 
 func isUselessPhi(phi *Phi) (Value, bool) {
 	var v0 Value
+	if len(phi.Edges) == 0 {
+		panic("φ with no edges")
+	}
 	for _, e := range phi.Edges {
+		if e == nil {
+			panic(fmt.Sprintf("φ has nil edge: %v", phi))
+		}
 		if e == phi {
 			continue
 		}
@@ -653,13 +502,12 @@ func isUselessPhi(phi *Phi) (Value, bool) {
 			return nil, false
 		}
 	}
+	if v0 == nil {
+		panic("trying to replace φ with nil")
+	}
 	return v0, true
 }
 
-func (f *Function) RemoveNilBlocks() {
-	f.removeNilBlocks()
-}
-
 // removeNilBlocks eliminates nils from f.Blocks and updates each
 // BasicBlock.Index.  Use this after any pass that may delete blocks.
 func (f *Function) removeNilBlocks() {
@@ -683,13 +531,14 @@ func (f *Function) removeNilBlocks() {
 // size of the instruction stream, and causes Functions to depend upon
 // the ASTs, potentially keeping them live in memory for longer.
 func (pkg *Package) SetDebugMode(debug bool) {
-	// TODO(adonovan): do we want ast.File granularity?
 	pkg.debug = debug
 }
 
 // debugInfo reports whether debug info is wanted for this function.
 func (f *Function) debugInfo() bool {
-	return f.Pkg != nil && f.Pkg.debug
+	// debug info for instantiations follows the debug info of their origin.
+	p := f.declaredPackage()
+	return p != nil && p.debug
 }
 
 // lookup returns the address of the named variable identified by obj
@@ -782,7 +631,7 @@ func (f *Function) RelString(from *types.Package) string {
 
 	// Thunk?
 	if f.method != nil {
-		return f.relMethod(from, f.method.Recv())
+		return f.relMethod(from, f.method.recv)
 	}
 
 	// Bound?
@@ -792,7 +641,7 @@ func (f *Function) RelString(from *types.Package) string {
 
 	// Package-level function?
 	// Prefix with package name for cross-package references only.
-	if p := f.pkg(); p != nil && p != from {
+	if p := f.relPkg(); p != nil && p != from {
 		return fmt.Sprintf("%s.%s", p.Path(), f.name)
 	}
 
@@ -820,9 +669,25 @@ func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *ty
 	types.WriteSignature(buf, sig, types.RelativeTo(from))
 }
 
-func (f *Function) pkg() *types.Package {
-	if f.Pkg != nil {
-		return f.Pkg.Pkg
+// declaredPackage returns the package fn is declared in or nil if the
+// function is not declared in a package.
+func (fn *Function) declaredPackage() *Package {
+	switch {
+	case fn.Pkg != nil:
+		return fn.Pkg // non-generic function  (does that follow??)
+	case fn.topLevelOrigin != nil:
+		return fn.topLevelOrigin.Pkg // instance of a named generic function
+	case fn.parent != nil:
+		return fn.parent.declaredPackage() // instance of an anonymous [generic] function
+	default:
+		return nil // function is not declared in a package, e.g. a wrapper.
+	}
+}
+
+// relPkg returns types.Package fn is printed in relationship to.
+func (fn *Function) relPkg() *types.Package {
+	if p := fn.declaredPackage(); p != nil {
+		return p.Pkg
 	}
 	return nil
 }
@@ -842,7 +707,7 @@ func WriteFunction(buf *bytes.Buffer, f *Function) {
 	if f.Pkg != nil {
 		fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Pkg.Path())
 	}
-	if syn := f.Synthetic; syn != 0 {
+	if syn := f.Synthetic; syn != "" {
 		fmt.Fprintln(buf, "# Synthetic:", syn)
 	}
 	if pos := f.Pos(); pos.IsValid() {
@@ -853,26 +718,32 @@ func WriteFunction(buf *bytes.Buffer, f *Function) {
 		fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name())
 	}
 
-	from := f.pkg()
+	from := f.relPkg()
+
+	// NB. column calculations are confused by non-ASCII
+	// characters
+	const punchcard = 80 // for old time's sake.
+	const padding = "        "
 
 	if f.FreeVars != nil {
 		buf.WriteString("# Free variables:\n")
 		for i, fv := range f.FreeVars {
-			fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), from))
+			fmt.Fprintf(buf, "# % 3d:%s%s %s\n", i, padding, fv.Name(), relType(fv.Type(), from))
 		}
 	}
 
 	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:%s%s %s\n", i, padding, l.Name(), relType(deref(l.Type()), from))
 		}
 	}
 	writeSignature(buf, from, f.Name(), f.Signature)
 	buf.WriteString(":\n")
 
 	if f.Blocks == nil {
-		buf.WriteString("\t(external)\n")
+		buf.WriteString(padding)
+		buf.WriteString("(external)\n")
 	}
 
 	for _, b := range f.Blocks {
@@ -881,86 +752,65 @@ func WriteFunction(buf *bytes.Buffer, f *Function) {
 			fmt.Fprintf(buf, ".nil:\n")
 			continue
 		}
-		fmt.Fprintf(buf, "b%d:", b.Index)
-		if len(b.Preds) > 0 {
-			fmt.Fprint(buf, " ←")
-			for _, pred := range b.Preds {
-				fmt.Fprintf(buf, " b%d", pred.Index)
-			}
-		}
-		if b.Comment != "" {
-			fmt.Fprintf(buf, " # %s", b.Comment)
+		n, _ := fmt.Fprintf(buf, "%d:", b.Index)
+		// (|predecessors|, |successors|, immediate dominator)
+		bmsg := fmt.Sprintf("%s P:%d S:%d", b.Comment, len(b.Preds), len(b.Succs))
+		if b.Idom() != nil {
+			bmsg = fmt.Sprintf("%s idom:%d", bmsg, b.Idom().Index)
 		}
-		buf.WriteByte('\n')
+		fmt.Fprintf(buf, "%*s%s\n", punchcard-1-n-len(bmsg), "", bmsg)
 
 		if false { // CFG debugging
-			fmt.Fprintf(buf, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs)
+			fmt.Fprintf(buf, "%s# CFG: %s --> %s --> %s\n", padding, b.Preds, b, b.Succs)
 		}
-
-		buf2 := &bytes.Buffer{}
 		for _, instr := range b.Instrs {
-			buf.WriteString("\t")
+			buf.WriteString(padding)
 			switch v := instr.(type) {
 			case Value:
+				l := punchcard - len(padding)
 				// Left-align the instruction.
 				if name := v.Name(); name != "" {
-					fmt.Fprintf(buf, "%s = ", name)
+					n, _ := fmt.Fprintf(buf, "%s = ", name)
+					l -= n
+				}
+				n, _ := buf.WriteString(instr.String())
+				l -= n
+				// Right-align the type if there's space.
+				if t := v.Type(); t != nil {
+					buf.WriteByte(' ')
+					ts := relType(t, from)
+					l -= len(ts) + len("  ") // (spaces before and after type)
+					if l > 0 {
+						fmt.Fprintf(buf, "%*s", l, "")
+					}
+					buf.WriteString(ts)
 				}
-				buf.WriteString(instr.String())
 			case nil:
 				// Be robust against bad transforms.
 				buf.WriteString("")
 			default:
 				buf.WriteString(instr.String())
 			}
-			if instr != nil && instr.Comment() != "" {
-				buf.WriteString(" # ")
-				buf.WriteString(instr.Comment())
-			}
-			buf.WriteString("\n")
-
-			if f.Prog.mode&PrintSource != 0 {
-				if s := instr.Source(); s != nil {
-					buf2.Reset()
-					format.Node(buf2, f.Prog.Fset, s)
-					for {
-						line, err := buf2.ReadString('\n')
-						if len(line) == 0 {
-							break
-						}
-						buf.WriteString("\t\t> ")
-						buf.WriteString(line)
-						if line[len(line)-1] != '\n' {
-							buf.WriteString("\n")
-						}
-						if err != nil {
-							break
-						}
-					}
+			// -mode=S: show line numbers
+			if f.Prog.mode&LogSource != 0 {
+				if pos := instr.Pos(); pos.IsValid() {
+					fmt.Fprintf(buf, " L%d", f.Prog.Fset.Position(pos).Line)
 				}
 			}
+			buf.WriteString("\n")
 		}
-		buf.WriteString("\n")
 	}
+	buf.WriteString("\n")
 }
 
 // newBasicBlock adds to f a new basic block and returns it.  It does
 // not automatically become the current block for subsequent calls to emit.
 // comment is an optional string for more readable debugging output.
 func (f *Function) newBasicBlock(comment string) *BasicBlock {
-	var instrs []Instruction
-	if len(f.functionBody.scratchInstructions) > 0 {
-		instrs = f.functionBody.scratchInstructions[0:0:avgInstructionsPerBlock]
-		f.functionBody.scratchInstructions = f.functionBody.scratchInstructions[avgInstructionsPerBlock:]
-	} else {
-		instrs = make([]Instruction, 0, avgInstructionsPerBlock)
-	}
-
 	b := &BasicBlock{
 		Index:   len(f.Blocks),
 		Comment: comment,
 		parent:  f,
-		Instrs:  instrs,
 	}
 	b.Succs = b.succs2[:0]
 	f.Blocks = append(f.Blocks, b)
@@ -981,7 +831,7 @@ func (f *Function) newBasicBlock(comment string) *BasicBlock {
 // "reflect" package, etc.
 //
 // TODO(adonovan): think harder about the API here.
-func (prog *Program) NewFunction(name string, sig *types.Signature, provenance Synthetic) *Function {
+func (prog *Program) NewFunction(name string, sig *types.Signature, provenance string) *Function {
 	return &Function{Prog: prog, name: name, Signature: sig, Synthetic: provenance}
 }
 
@@ -991,15 +841,6 @@ type extentNode [2]token.Pos
 func (n extentNode) Pos() token.Pos { return n[0] }
 func (n extentNode) End() token.Pos { return n[1] }
 
-func (f *Function) initHTML(name string) {
-	if name == "" {
-		return
-	}
-	if rel := f.RelString(nil); rel == name {
-		f.wr = NewHTMLWriter("ir.html", rel, "")
-	}
-}
-
 func killInstruction(instr Instruction) {
 	ops := instr.Operands(nil)
 	for _, op := range ops {
@@ -1009,9 +850,14 @@ func killInstruction(instr Instruction) {
 	}
 }
 
+// Syntax returns the function's syntax (*ast.Func{Decl,Lit})
+// if it was produced from syntax or an *ast.RangeStmt if
+// it is a range-over-func yield function.
+func (f *Function) Syntax() ast.Node { return f.syntax }
+
 // identVar returns the variable defined by id.
 func identVar(fn *Function, id *ast.Ident) *types.Var {
-	return fn.Pkg.info.Defs[id].(*types.Var)
+	return fn.info.Defs[id].(*types.Var)
 }
 
 // unique returns a unique positive int within the source tree of f.
@@ -1082,7 +928,7 @@ func returnExit(fn *Function, source ast.Node) *exit {
 	e := &exit{
 		id:     unique(fn),
 		from:   fn,
-		to:     fn.sourceFn,
+		to:     fn.source,
 		source: source,
 	}
 	fn.exits = append(fn.exits, e)
diff --git a/vendor/honnef.co/go/tools/go/ir/html.go b/vendor/honnef.co/go/tools/go/ir/html.go
deleted file mode 100644
index 86b2a63d..00000000
--- a/vendor/honnef.co/go/tools/go/ir/html.go
+++ /dev/null
@@ -1,1126 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Copyright 2019 Dominik Honnef. All rights reserved.
-
-package ir
-
-import (
-	"bytes"
-	"fmt"
-	"go/types"
-	"html"
-	"io"
-	"log"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"reflect"
-	"sort"
-	"strings"
-)
-
-func live(f *Function) []bool {
-	max := 0
-	var ops []*Value
-
-	for _, b := range f.Blocks {
-		for _, instr := range b.Instrs {
-			if int(instr.ID()) > max {
-				max = int(instr.ID())
-			}
-		}
-	}
-
-	out := make([]bool, max+1)
-	var q []Node
-	for _, b := range f.Blocks {
-		for _, instr := range b.Instrs {
-			switch instr.(type) {
-			case *BlankStore, *Call, *ConstantSwitch, *Defer, *Go, *If, *Jump, *MapUpdate, *Next, *Panic, *Recv, *Return, *RunDefers, *Send, *Store, *Unreachable:
-				out[instr.ID()] = true
-				q = append(q, instr)
-			}
-		}
-	}
-
-	for len(q) > 0 {
-		v := q[len(q)-1]
-		q = q[:len(q)-1]
-		for _, op := range v.Operands(ops) {
-			if *op == nil {
-				continue
-			}
-			if !out[(*op).ID()] {
-				out[(*op).ID()] = true
-				q = append(q, *op)
-			}
-		}
-	}
-
-	return out
-}
-
-type funcPrinter interface {
-	startBlock(b *BasicBlock, reachable bool)
-	endBlock(b *BasicBlock)
-	value(v Node, live bool)
-	startDepCycle()
-	endDepCycle()
-	named(n string, vals []Value)
-}
-
-func namedValues(f *Function) map[types.Object][]Value {
-	names := map[types.Object][]Value{}
-	for _, b := range f.Blocks {
-		for _, instr := range b.Instrs {
-			if instr, ok := instr.(*DebugRef); ok {
-				if obj := instr.object; obj != nil {
-					names[obj] = append(names[obj], instr.X)
-				}
-			}
-		}
-	}
-	// XXX deduplicate values
-	return names
-}
-
-func fprintFunc(p funcPrinter, f *Function) {
-	// XXX does our IR form preserve unreachable blocks?
-	// reachable, live := findlive(f)
-
-	l := live(f)
-	for _, b := range f.Blocks {
-		// XXX
-		// p.startBlock(b, reachable[b.Index])
-		p.startBlock(b, true)
-
-		end := max(len(b.Instrs)-1, 0)
-		for _, v := range b.Instrs[:end] {
-			if _, ok := v.(*DebugRef); !ok {
-				p.value(v, l[v.ID()])
-			}
-		}
-		p.endBlock(b)
-	}
-
-	names := namedValues(f)
-	keys := make([]types.Object, 0, len(names))
-	for key := range names {
-		keys = append(keys, key)
-	}
-	sort.Slice(keys, func(i, j int) bool {
-		return keys[i].Pos() < keys[j].Pos()
-	})
-	for _, key := range keys {
-		p.named(key.Name(), names[key])
-	}
-}
-
-func opName(v Node) string {
-	switch v := v.(type) {
-	case *Call:
-		if v.Common().IsInvoke() {
-			return "Invoke"
-		}
-		return "Call"
-	case *Alloc:
-		if v.Heap {
-			return "HeapAlloc"
-		}
-		return "StackAlloc"
-	case *Select:
-		if v.Blocking {
-			return "SelectBlocking"
-		}
-		return "SelectNonBlocking"
-	default:
-		return reflect.ValueOf(v).Type().Elem().Name()
-	}
-}
-
-type HTMLWriter struct {
-	w    io.WriteCloser
-	path string
-	dot  *dotWriter
-}
-
-func NewHTMLWriter(path string, funcname, cfgMask string) *HTMLWriter {
-	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
-	if err != nil {
-		log.Fatalf("%v", err)
-	}
-	pwd, err := os.Getwd()
-	if err != nil {
-		log.Fatalf("%v", err)
-	}
-	html := HTMLWriter{w: out, path: filepath.Join(pwd, path)}
-	html.dot = newDotWriter()
-	html.start(funcname)
-	return &html
-}
-
-func (w *HTMLWriter) start(name string) {
-	if w == nil {
-		return
-	}
-	w.WriteString("")
-	w.WriteString(`
-
-
-
-
-
-`)
-	w.WriteString("")
-	w.WriteString("

") - w.WriteString(html.EscapeString(name)) - w.WriteString("

") - w.WriteString(` -help -
- -

-Click on a value or block to toggle highlighting of that value/block -and its uses. (Values and blocks are highlighted by ID, and IDs of -dead items may be reused, so not all highlights necessarily correspond -to the clicked item.) -

- -

-Faded out values and blocks are dead code that has not been eliminated. -

- -

-Values printed in italics have a dependency cycle. -

- -

-CFG: Dashed edge is for unlikely branches. Blue color is for backward edges. -Edge with a dot means that this edge follows the order in which blocks were laid out. -

- -
-`) - w.WriteString("") - w.WriteString("") -} - -func (w *HTMLWriter) Close() { - if w == nil { - return - } - io.WriteString(w.w, "") - io.WriteString(w.w, "
") - io.WriteString(w.w, "") - io.WriteString(w.w, "") - w.w.Close() - fmt.Printf("dumped IR to %v\n", w.path) -} - -// WriteFunc writes f in a column headed by title. -// phase is used for collapsing columns and should be unique across the table. -func (w *HTMLWriter) WriteFunc(phase, title string, f *Function) { - if w == nil { - return - } - w.WriteColumn(phase, title, "", funcHTML(f, phase, w.dot)) -} - -// WriteColumn writes raw HTML in a column headed by title. -// It is intended for pre- and post-compilation log output. -func (w *HTMLWriter) WriteColumn(phase, title, class, html string) { - if w == nil { - return - } - id := strings.Replace(phase, " ", "-", -1) - // collapsed column - w.Printf("
%v
", id, phase) - - if class == "" { - w.Printf("", id) - } else { - w.Printf("", id, class) - } - w.WriteString("

" + title + "

") - w.WriteString(html) - w.WriteString("") -} - -func (w *HTMLWriter) Printf(msg string, v ...any) { - if _, err := fmt.Fprintf(w.w, msg, v...); err != nil { - log.Fatalf("%v", err) - } -} - -func (w *HTMLWriter) WriteString(s string) { - if _, err := io.WriteString(w.w, s); err != nil { - log.Fatalf("%v", err) - } -} - -func valueHTML(v Node) string { - if v == nil { - return "<nil>" - } - // TODO: Using the value ID as the class ignores the fact - // that value IDs get recycled and that some values - // are transmuted into other values. - class := fmt.Sprintf("t%d", v.ID()) - var label string - switch v := v.(type) { - case *Function: - label = v.RelString(nil) - case *Builtin: - label = v.Name() - default: - label = class - } - return fmt.Sprintf("%s", class, label) -} - -func valueLongHTML(v Node) string { - // TODO: Any intra-value formatting? - // I'm wary of adding too much visual noise, - // but a little bit might be valuable. - // We already have visual noise in the form of punctuation - // maybe we could replace some of that with formatting. - var s strings.Builder - s.WriteString(fmt.Sprintf("", v.ID())) - - linenumber := "(?)" - if v.Pos().IsValid() { - line := v.Parent().Prog.Fset.Position(v.Pos()).Line - linenumber = fmt.Sprintf("(%d)", line, line) - } - - s.WriteString(fmt.Sprintf("%s %s = %s", valueHTML(v), linenumber, opName(v))) - - if v, ok := v.(Value); ok { - s.WriteString(" <" + html.EscapeString(v.Type().String()) + ">") - } - - switch v := v.(type) { - case *Parameter: - s.WriteString(fmt.Sprintf(" {%s}", html.EscapeString(v.name))) - case *BinOp: - s.WriteString(fmt.Sprintf(" {%s}", html.EscapeString(v.Op.String()))) - case *UnOp: - s.WriteString(fmt.Sprintf(" {%s}", html.EscapeString(v.Op.String()))) - case *Extract: - name := v.Tuple.Type().(*types.Tuple).At(v.Index).Name() - s.WriteString(fmt.Sprintf(" [%d] (%s)", v.Index, name)) - case *Field: - st := v.X.Type().Underlying().(*types.Struct) - // Be robust against a bad index. - name := "?" - if 0 <= v.Field && v.Field < st.NumFields() { - name = st.Field(v.Field).Name() - } - s.WriteString(fmt.Sprintf(" [%d] (%s)", v.Field, name)) - case *FieldAddr: - st := deref(v.X.Type()).Underlying().(*types.Struct) - // Be robust against a bad index. - name := "?" - if 0 <= v.Field && v.Field < st.NumFields() { - name = st.Field(v.Field).Name() - } - - s.WriteString(fmt.Sprintf(" [%d] (%s)", v.Field, name)) - case *Recv: - s.WriteString(fmt.Sprintf(" {%t}", v.CommaOk)) - case *Call: - if v.Common().IsInvoke() { - s.WriteString(fmt.Sprintf(" {%s}", html.EscapeString(v.Common().Method.FullName()))) - } - case *Const: - if v.Value == nil { - s.WriteString(" {<nil>}") - } else { - s.WriteString(fmt.Sprintf(" {%s}", html.EscapeString(v.Value.String()))) - } - case *Sigma: - s.WriteString(fmt.Sprintf(" [#%s]", v.From)) - } - for _, a := range v.Operands(nil) { - s.WriteString(fmt.Sprintf(" %s", valueHTML(*a))) - } - if v, ok := v.(Instruction); ok { - s.WriteString(fmt.Sprintf(" (%s)", v.Comment())) - } - - // OPT(dh): we're calling namedValues many times on the same function. - allNames := namedValues(v.Parent()) - var names []string - for name, values := range allNames { - for _, value := range values { - if v == value { - names = append(names, name.Name()) - break - } - } - } - if len(names) != 0 { - s.WriteString(" (" + strings.Join(names, ", ") + ")") - } - - s.WriteString("") - return s.String() -} - -func blockHTML(b *BasicBlock) string { - // TODO: Using the value ID as the class ignores the fact - // that value IDs get recycled and that some values - // are transmuted into other values. - s := html.EscapeString(b.String()) - return fmt.Sprintf("%s", s, s) -} - -func blockLongHTML(b *BasicBlock) string { - var kind string - var term Instruction - if len(b.Instrs) > 0 { - term = b.Control() - kind = opName(term) - } - // TODO: improve this for HTML? - var s strings.Builder - s.WriteString(fmt.Sprintf("%s", b.Index, kind)) - - if term != nil { - ops := term.Operands(nil) - if len(ops) > 0 { - var ss []string - for _, op := range ops { - ss = append(ss, valueHTML(*op)) - } - s.WriteString(" " + strings.Join(ss, ", ")) - } - } - if len(b.Succs) > 0 { - s.WriteString(" →") // right arrow - for _, c := range b.Succs { - s.WriteString(" " + blockHTML(c)) - } - } - return s.String() -} - -func funcHTML(f *Function, phase string, dot *dotWriter) string { - buf := new(bytes.Buffer) - if dot != nil { - dot.writeFuncSVG(buf, phase, f) - } - fmt.Fprint(buf, "") - p := htmlFuncPrinter{w: buf} - fprintFunc(p, f) - - // fprintFunc(&buf, f) // TODO: HTML, not text,
for line breaks, etc. - fmt.Fprint(buf, "
") - return buf.String() -} - -type htmlFuncPrinter struct { - w io.Writer -} - -func (p htmlFuncPrinter) startBlock(b *BasicBlock, reachable bool) { - var dead string - if !reachable { - dead = "dead-block" - } - fmt.Fprintf(p.w, "
    ", b, dead) - fmt.Fprintf(p.w, "
  • %s:", blockHTML(b)) - if len(b.Preds) > 0 { - io.WriteString(p.w, " ←") // left arrow - for _, pred := range b.Preds { - fmt.Fprintf(p.w, " %s", blockHTML(pred)) - } - } - if len(b.Instrs) > 0 { - io.WriteString(p.w, ``) - } - io.WriteString(p.w, "
  • ") - if len(b.Instrs) > 0 { // start list of values - io.WriteString(p.w, "
  • ") - io.WriteString(p.w, "
      ") - } -} - -func (p htmlFuncPrinter) endBlock(b *BasicBlock) { - if len(b.Instrs) > 0 { // end list of values - io.WriteString(p.w, "
    ") - io.WriteString(p.w, "
  • ") - } - io.WriteString(p.w, "
  • ") - fmt.Fprint(p.w, blockLongHTML(b)) - io.WriteString(p.w, "
  • ") - io.WriteString(p.w, "
") -} - -func (p htmlFuncPrinter) value(v Node, live bool) { - var dead string - if !live { - dead = "dead-value" - } - fmt.Fprintf(p.w, "
  • ", dead) - fmt.Fprint(p.w, valueLongHTML(v)) - io.WriteString(p.w, "
  • ") -} - -func (p htmlFuncPrinter) startDepCycle() { - fmt.Fprintln(p.w, "") -} - -func (p htmlFuncPrinter) endDepCycle() { - fmt.Fprintln(p.w, "") -} - -func (p htmlFuncPrinter) named(n string, vals []Value) { - fmt.Fprintf(p.w, "
  • name %s: ", n) - for _, val := range vals { - fmt.Fprintf(p.w, "%s ", valueHTML(val)) - } - fmt.Fprintf(p.w, "
  • ") -} - -type dotWriter struct { - path string - broken bool -} - -// newDotWriter returns non-nil value when mask is valid. -// dotWriter will generate SVGs only for the phases specified in the mask. -// mask can contain following patterns and combinations of them: -// * - all of them; -// x-y - x through y, inclusive; -// x,y - x and y, but not the passes between. -func newDotWriter() *dotWriter { - path, err := exec.LookPath("dot") - if err != nil { - fmt.Println(err) - return nil - } - return &dotWriter{path: path} -} - -func (d *dotWriter) writeFuncSVG(w io.Writer, phase string, f *Function) { - if d.broken { - return - } - cmd := exec.Command(d.path, "-Tsvg") - pipe, err := cmd.StdinPipe() - if err != nil { - d.broken = true - fmt.Println(err) - return - } - buf := new(bytes.Buffer) - cmd.Stdout = buf - bufErr := new(bytes.Buffer) - cmd.Stderr = bufErr - err = cmd.Start() - if err != nil { - d.broken = true - fmt.Println(err) - return - } - fmt.Fprint(pipe, `digraph "" { margin=0; size="4,40"; ranksep=.2; `) - id := strings.Replace(phase, " ", "-", -1) - fmt.Fprintf(pipe, `id="g_graph_%s";`, id) - fmt.Fprintf(pipe, `node [style=filled,fillcolor=white,fontsize=16,fontname="Menlo,Times,serif",margin="0.01,0.03"];`) - fmt.Fprintf(pipe, `edge [fontsize=16,fontname="Menlo,Times,serif"];`) - for _, b := range f.Blocks { - layout := "" - fmt.Fprintf(pipe, `%v [label="%v%s\n%v",id="graph_node_%v_%v"];`, b, b, layout, b.Control().String(), id, b) - } - indexOf := make([]int, len(f.Blocks)) - for i, b := range f.Blocks { - indexOf[b.Index] = i - } - - // XXX - /* - ponums := make([]int32, len(f.Blocks)) - _ = postorderWithNumbering(f, ponums) - isBackEdge := func(from, to int) bool { - return ponums[from] <= ponums[to] - } - */ - isBackEdge := func(from, to int) bool { return false } - - for _, b := range f.Blocks { - for i, s := range b.Succs { - style := "solid" - color := "black" - arrow := "vee" - if isBackEdge(b.Index, s.Index) { - color = "blue" - } - fmt.Fprintf(pipe, `%v -> %v [label=" %d ",style="%s",color="%s",arrowhead="%s"];`, b, s, i, style, color, arrow) - } - } - fmt.Fprint(pipe, "}") - pipe.Close() - err = cmd.Wait() - if err != nil { - d.broken = true - fmt.Printf("dot: %v\n%v\n", err, bufErr.String()) - return - } - - svgID := "svg_graph_" + id - fmt.Fprintf(w, `
    `, svgID, svgID) - // For now, an awful hack: edit the html as it passes through - // our fingers, finding ' 0 || len(targs) > 0 + if len(rtargs) > 0 { + // possibly generic method on generic type + obj = prog.canon.instantiateMethod(fn.object, rtargs, prog.ctxt) + } else { + // generic method on non-generic type + obj = fn.object // instantiation does not exist yet + } + if len(targs) > 0 { + // generic method + instSig, err := types.Instantiate(prog.ctxt, obj.Signature(), targs, false) + if err != nil { + panic(err) + } + instance, ok := instSig.(*types.Signature) + if !ok { + panic("Instantiate of a Signature returned a non-signature") + } + // Do not canonicalize generic methods, because the receiver is not + // part of a Signature's type identity. For example, + // (*G[int]).m[int] and (G[int]).n[int] may be identical types even + // though their receiver types differ. + sig = instance + } else { + // non-generic method on generic type + sig = obj.Signature() + } + } else { + // function, len(rtargs) == 0 && len(targs) > 0 + instSig, err := types.Instantiate(prog.ctxt, fn.Signature, targs, false) + if err != nil { + panic(err) + } + instance, ok := instSig.(*types.Signature) + if !ok { + panic("Instantiate of a Signature returned a non-signature") + } + obj = fn.object // instantiation does not exist yet + sig = prog.canon.Type(instance).(*types.Signature) + } + + // Choose strategy (instance or wrapper). + var ( + synthetic string + subst *subster + build buildFunc + ) + if prog.mode&InstantiateGenerics != 0 && !prog.isParameterized(slices.Concat(rtargs, targs)...) { + synthetic = fmt.Sprintf("instance of %s", fn.Name()) + if fn.syntax != nil { + subst = makeSubster(prog.ctxt, obj, fn.recvtypeparams, rtargs, fn.typeparams, targs) + build = (*builder).buildFromSyntax + } else { + build = (*builder).buildParamsOnly + } + } else { + synthetic = fmt.Sprintf("instantiation wrapper of %s", fn.Name()) + build = (*builder).buildInstantiationWrapper + } + + name := fn.Name() + if len(targs) > 0 { + name = fmt.Sprintf("%s%s", name, targstr(targs)) // may not be unique + } + + /* generic instance or instantiation wrapper */ + ret := &Function{ + name: name, + object: obj, + Signature: sig, + Synthetic: synthetic, + syntax: fn.syntax, // \ + info: fn.info, // } empty for non-created packages + goversion: fn.goversion, // / + build: build, + topLevelOrigin: fn, + pos: obj.Pos(), + Pkg: nil, + Prog: fn.Prog, + recvtypeparams: fn.recvtypeparams, // share with origin + recvtypeargs: rtargs, + typeparams: fn.typeparams, // share with origin + typeargs: targs, + subst: subst, + } + return ret +} + +// isParameterized reports whether any of the specified types contains +// a free type parameter. It is safe to call concurrently. +func (prog *Program) isParameterized(ts ...types.Type) bool { + prog.hasParamsMu.Lock() + defer prog.hasParamsMu.Unlock() + + // TODO(adonovan): profile. If this operation is expensive, + // handle the most common but shallow cases such as T, pkg.T, + // *T without consulting the cache under the lock. + + return slices.ContainsFunc(ts, prog.hasParams.Has) +} diff --git a/vendor/honnef.co/go/tools/go/ir/irutil/load.go b/vendor/honnef.co/go/tools/go/ir/irutil/load.go index bbf0e271..49eaa722 100644 --- a/vendor/honnef.co/go/tools/go/ir/irutil/load.go +++ b/vendor/honnef.co/go/tools/go/ir/irutil/load.go @@ -13,16 +13,9 @@ import ( "honnef.co/go/tools/go/ir" - //lint:ignore SA1019 go/loader is deprecated, but works fine for our tests - "golang.org/x/tools/go/loader" "golang.org/x/tools/go/packages" ) -type Options struct { - // Which function, if any, to print in HTML form - PrintFunc string -} - // Packages creates an IR program for a set of packages. // // The packages must have been loaded from source syntax using the @@ -40,8 +33,26 @@ type Options struct { // packages with well-typed syntax trees. // // The mode parameter controls diagnostics and checking during IR construction. -func Packages(initial []*packages.Package, mode ir.BuilderMode, opts *Options) (*ir.Program, []*ir.Package) { - return doPackages(initial, mode, false, opts) +func Packages(initial []*packages.Package, mode ir.BuilderMode) (*ir.Program, []*ir.Package) { + // TODO(adonovan): opt: this calls CreatePackage far more than + // necessary: for all dependencies, not just the (non-initial) + // direct dependencies of the initial packages. + // + // But can it reasonably be changed without breaking the + // spirit and/or letter of the law above? Clients may notice + // if we call CreatePackage less, as methods like + // Program.FuncValue will return nil. Or must we provide a new + // function (and perhaps deprecate this one)? Is it worth it? + // + // Tim King makes the interesting point that it would be + // possible to entirely alleviate the client from the burden + // of calling CreatePackage for non-syntax packages, if we + // were to treat vars and funcs lazily in the same way we now + // treat methods. (In essence, try to move away from the + // notion of ir.Packages, and make the Program answer + // all reasonable questions about any types.Object.) + + return doPackages(initial, mode, false) } // AllPackages creates an IR program for a set of packages plus all @@ -61,21 +72,17 @@ func Packages(initial []*packages.Package, mode ir.BuilderMode, opts *Options) ( // well-typed syntax trees. // // The mode parameter controls diagnostics and checking during IR construction. -func AllPackages(initial []*packages.Package, mode ir.BuilderMode, opts *Options) (*ir.Program, []*ir.Package) { - return doPackages(initial, mode, true, opts) +func AllPackages(initial []*packages.Package, mode ir.BuilderMode) (*ir.Program, []*ir.Package) { + return doPackages(initial, mode, true) } -func doPackages(initial []*packages.Package, mode ir.BuilderMode, deps bool, opts *Options) (*ir.Program, []*ir.Package) { - +func doPackages(initial []*packages.Package, mode ir.BuilderMode, deps bool) (*ir.Program, []*ir.Package) { var fset *token.FileSet if len(initial) > 0 { fset = initial[0].Fset } prog := ir.NewProgram(fset, mode) - if opts != nil { - prog.PrintFunc = opts.PrintFunc - } isInitial := make(map[*packages.Package]bool, len(initial)) for _, p := range initial { @@ -86,10 +93,12 @@ func doPackages(initial []*packages.Package, mode ir.BuilderMode, deps bool, opt packages.Visit(initial, nil, func(p *packages.Package) { if p.Types != nil && !p.IllTyped { var files []*ast.File + var info *types.Info if deps || isInitial[p] { files = p.Syntax + info = p.TypesInfo } - irmap[p] = prog.CreatePackage(p.Types, files, p.TypesInfo, true) + irmap[p] = prog.CreatePackage(p.Types, files, info, true) } }) @@ -100,30 +109,8 @@ func doPackages(initial []*packages.Package, mode ir.BuilderMode, deps bool, opt return prog, irpkgs } -// CreateProgram returns a new program in IR form, given a program -// loaded from source. An IR package is created for each transitively -// error-free package of lprog. -// -// Code for bodies of functions is not built until Build is called -// on the result. -// -// The mode parameter controls diagnostics and checking during IR construction. -// -// Deprecated: use golang.org/x/tools/go/packages and the Packages -// function instead; see ir.ExampleLoadPackages. -func CreateProgram(lprog *loader.Program, mode ir.BuilderMode) *ir.Program { - prog := ir.NewProgram(lprog.Fset, mode) - - for _, info := range lprog.AllPackages { - if info.TransitivelyErrorFree { - prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable) - } - } - - return prog -} - -// BuildPackage builds an IR program with IR for a single package. +// BuildPackage builds an IR program with IR intermediate +// representation (IR) for all functions of a single package. // // It populates pkg by type-checking the specified file ASTs. All // dependencies are loaded using the importer specified by tc, which @@ -176,6 +163,25 @@ func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, fil } createAll(pkg.Imports()) + // TODO(adonovan): we could replace createAll with just: + // + // // Create IR packages for all imports. + // for _, p := range pkg.Imports() { + // prog.CreatePackage(p, nil, nil, true) + // } + // + // (with minor changes to changes to ../builder_test.go as + // shown in CL 511715 PS 10.) But this would strictly violate + // the letter of the doc comment above, which says "all + // dependencies created". + // + // Tim makes the good point with some extra work we could + // remove the need for any CreatePackage calls except the + // ones with syntax (i.e. primary packages). Of course + // You wouldn't have ir.Packages and Members for as + // many things but no-one really uses that anyway. + // I wish I had done this from the outset. + // Create and build the primary package. irpkg := prog.CreatePackage(pkg, files, info, false) irpkg.Build() diff --git a/vendor/honnef.co/go/tools/go/ir/irutil/stub.go b/vendor/honnef.co/go/tools/go/ir/irutil/stub.go index 4311c7db..a686ede6 100644 --- a/vendor/honnef.co/go/tools/go/ir/irutil/stub.go +++ b/vendor/honnef.co/go/tools/go/ir/irutil/stub.go @@ -6,22 +6,87 @@ import ( // IsStub reports whether a function is a stub. A function is // considered a stub if it has no instructions or if all it does is -// return a constant value. +// return a fixed value (either an actual constant or a new allocation). func IsStub(fn *ir.Function) bool { + if fn.Source() == nil { + // External functions have to be assumed to not be stubs. + return false + } + for _, b := range fn.Blocks { for _, instr := range b.Instrs { switch instr.(type) { - case *ir.Const: - // const naturally has no side-effects case *ir.Panic: // panic is a stub if it only uses constants case *ir.Return: // return is a stub if it only uses constants - case *ir.DebugRef: case *ir.Jump: // if there are no disallowed instructions, then we're // only jumping to the exit block (or possibly // somewhere else that's stubby?) + case *ir.MakeInterface: + // this can only be wrapping Const and HeapAlloc, which is fine. + case *ir.Alloc: + // allocating is fine as long as all we do with the allocation + // is return it (possibly in an interface value) + default: + // all other instructions are assumed to do actual work + return false + } + } + } + return true +} + +// IsTrivial reports whether a function is trivial. A function is +// considered trivial if it is a stub, only returns a global variable, or calls +// another trivial function. +func IsTrivial(fn *ir.Function) bool { + return isTrivial(fn, nil) +} + +func isTrivial(fn *ir.Function, seen map[*ir.Function]struct{}) bool { + if fn.Source() == nil { + // External functions have to be assumed to be nontrivial. + return false + } + + if _, ok := seen[fn]; ok { + // Mutual recursion is not trivial + return false + } + + // We delay adding to seen until a call, to avoid creating garbage in the + // common case. + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + switch instr := instr.(type) { + case *ir.Panic: + // panic is a stub if it only uses constants + case *ir.Return: + // return is a stub if it only uses constants + case *ir.Jump: + // if there are no disallowed instructions, then we're + // only jumping to the exit block (or possibly + // somewhere else that's stubby?) + case *ir.MakeInterface: + // this can only be wrapping allowed instructions + case *ir.Alloc: + // allocating is fine as long as all we do with the allocation + // is return it (possibly in an interface value) + case *ir.Load: + if _, ok := instr.X.(*ir.Global); !ok { + return false + } + case *ir.Call: + if seen == nil { + seen = make(map[*ir.Function]struct{}) + } + seen[fn] = struct{}{} + callee := instr.Call.StaticCallee() + if callee == nil || !isTrivial(callee, seen) { + return false + } default: // all other instructions are assumed to do actual work return false diff --git a/vendor/honnef.co/go/tools/go/ir/irutil/switch.go b/vendor/honnef.co/go/tools/go/ir/irutil/switch.go index afe899d8..cf7c54e4 100644 --- a/vendor/honnef.co/go/tools/go/ir/irutil/switch.go +++ b/vendor/honnef.co/go/tools/go/ir/irutil/switch.go @@ -129,20 +129,10 @@ func Switches(fn *ir.Function) []Switch { return switches } -func isSameX(x1 ir.Value, x2 ir.Value) bool { - if x1 == x2 { - return true - } - if x2, ok := x2.(*ir.Sigma); ok { - return isSameX(x1, x2.X) - } - return false -} - func valueSwitch(sw *Switch, k *ir.Const, seen map[*ir.BasicBlock]bool) { b := sw.Start x := sw.X - for isSameX(sw.X, x) { + for sw.X == x { if seen[b] { break } @@ -159,13 +149,13 @@ func valueSwitch(sw *Switch, k *ir.Const, seen map[*ir.BasicBlock]bool) { switch instr.(type) { case *ir.If, *ir.BinOp: n++ - case *ir.Sigma, *ir.Phi, *ir.DebugRef: + case *ir.Phi: default: n += 1000 } } if n != 2 { - // Block b contains not just 'if x == k' and σ/ϕ nodes, + // Block b contains not just 'if x == k' and ϕ nodes, // so it may have side effects that // make it unsafe to elide. break @@ -183,7 +173,7 @@ func valueSwitch(sw *Switch, k *ir.Const, seen map[*ir.BasicBlock]bool) { func typeSwitch(sw *Switch, y ir.Value, T types.Type, seen map[*ir.BasicBlock]bool) { b := sw.Start x := sw.X - for isSameX(sw.X, x) { + for sw.X == x { if seen[b] { break } @@ -201,7 +191,7 @@ func typeSwitch(sw *Switch, y ir.Value, T types.Type, seen map[*ir.BasicBlock]bo switch instr.(type) { case *ir.TypeAssert, *ir.Extract, *ir.If: n++ - case *ir.Sigma, *ir.Phi: + case *ir.Phi: default: n += 1000 } diff --git a/vendor/honnef.co/go/tools/go/ir/irutil/util.go b/vendor/honnef.co/go/tools/go/ir/irutil/util.go index 9fed28bb..c8b980b3 100644 --- a/vendor/honnef.co/go/tools/go/ir/irutil/util.go +++ b/vendor/honnef.co/go/tools/go/ir/irutil/util.go @@ -47,42 +47,31 @@ func Walk(b *ir.BasicBlock, fn func(*ir.BasicBlock) bool) { func Vararg(x *ir.Slice) ([]ir.Value, bool) { var out []ir.Value - alloc, ok := ir.Unwrap(x.X).(*ir.Alloc) + alloc, ok := x.X.(*ir.Alloc) if !ok { return nil, false } - var checkAlloc func(alloc ir.Value) bool - checkAlloc = func(alloc ir.Value) bool { - for _, ref := range *alloc.Referrers() { - if ref == x { - continue - } - if ref.Block() != x.Block() { - return false + for _, ref := range *alloc.Referrers() { + if ref == x { + continue + } + if ref.Block() != x.Block() { + return nil, false + } + switch ref := ref.(type) { + case *ir.IndexAddr: + idx := ref + if len(*idx.Referrers()) != 1 { + return nil, false } - switch ref := ref.(type) { - case *ir.IndexAddr: - idx := ref - if len(*idx.Referrers()) != 1 { - return false - } - store, ok := (*idx.Referrers())[0].(*ir.Store) - if !ok { - return false - } - out = append(out, store.Val) - case *ir.Copy: - if !checkAlloc(ref) { - return false - } - default: - return false + store, ok := (*idx.Referrers())[0].(*ir.Store) + if !ok { + return nil, false } + out = append(out, store.Val) + default: + return nil, false } - return true - } - if !checkAlloc(alloc) { - return nil, false } return out, true } @@ -111,16 +100,6 @@ func IsCallToAny(call *ir.CallCommon, names ...string) bool { return slices.Contains(names, q) } -func FilterDebug(instr []ir.Instruction) []ir.Instruction { - var out []ir.Instruction - for _, ins := range instr { - if _, ok := ins.(*ir.DebugRef); !ok { - out = append(out, ins) - } - } - return out -} - func IsExample(fn *ir.Function) bool { if !strings.HasPrefix(fn.Name(), "Example") { return false @@ -132,7 +111,7 @@ func IsExample(fn *ir.Function) bool { return strings.HasSuffix(f.Name(), "_test.go") } -// Flatten recursively returns the underlying value of an ir.Sigma or +// Flatten recursively returns the underlying value of an // ir.Phi node. If all edges in an ir.Phi node are the same (after // flattening), the flattened edge will get returned. If flattening is // not possible, nil is returned. @@ -151,8 +130,6 @@ func Flatten(v ir.Value) ir.Value { seen[v] = struct{}{} switch v := v.(type) { - case *ir.Sigma: - dfs(v.X) case *ir.Phi: for _, e := range v.Edges { dfs(e) diff --git a/vendor/honnef.co/go/tools/go/ir/irutil/visit.go b/vendor/honnef.co/go/tools/go/ir/irutil/visit.go index f2135dca..ef438a9a 100644 --- a/vendor/honnef.co/go/tools/go/ir/irutil/visit.go +++ b/vendor/honnef.co/go/tools/go/ir/irutil/visit.go @@ -2,9 +2,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package irutil +package irutil // import "honnef.co/go/tools/go/ir/irutil" -import "honnef.co/go/tools/go/ir" +import ( + "go/ast" + "go/types" + + _ "unsafe" // for linkname hack + + "honnef.co/go/tools/go/ir" +) // This file defines utilities for visiting the IR of // a Program. @@ -18,50 +25,122 @@ import "honnef.co/go/tools/go/ir" // synthetic wrappers. // // Precondition: all packages are built. +// +// TODO(adonovan): this function is underspecified. It doesn't +// actually work like a linker, which computes reachability from main +// using something like go/callgraph/cha (without materializing the +// call graph). In fact, it treats all public functions and all +// methods of public non-parameterized types as roots, even though +// they may be unreachable--but only in packages created from syntax. +// +// I think we should deprecate AllFunctions function in favor of two +// clearly defined ones: +// +// 1. The first would efficiently compute CHA reachability from a set +// of main packages, making it suitable for a whole-program +// analysis context with InstantiateGenerics, in conjunction with +// Program.Build. +// +// 2. The second would return only the set of functions corresponding +// to source Func{Decl,Lit} syntax, like SrcFunctions in +// go/analysis/passes/buildssa; this is suitable for +// package-at-a-time (or handful of packages) context. +// ir.Package could easily expose it as a field. +// +// We could add them unexported for now and use them via the linkname hack. func AllFunctions(prog *ir.Program) map[*ir.Function]bool { - visit := visitor{ - prog: prog, - seen: make(map[*ir.Function]bool), + seen := make(map[*ir.Function]bool) + + var function func(fn *ir.Function) + function = func(fn *ir.Function) { + if !seen[fn] { + seen[fn] = true + var buf [10]*ir.Value // avoid alloc in common case + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + for _, op := range instr.Operands(buf[:0]) { + if fn, ok := (*op).(*ir.Function); ok { + function(fn) + } + } + } + } + } } - visit.program() - return visit.seen -} -type visitor struct { - prog *ir.Program - seen map[*ir.Function]bool -} + // TODO(adonovan): opt: provide a way to share a builder + // across a sequence of MethodValue calls. -func (visit *visitor) program() { - for _, pkg := range visit.prog.AllPackages() { - for _, mem := range pkg.Members { - if fn, ok := mem.(*ir.Function); ok { - visit.function(fn) + methodsOf := func(T types.Type) { + if !types.IsInterface(T) { + mset := prog.MethodSets.MethodSet(T) + for sel := range mset.Methods() { + // Skip generic methods. + if sel.Obj().(*types.Func).Signature().TypeParams() == nil { + function(prog.MethodValue(sel)) + } } } } - for _, T := range visit.prog.RuntimeTypes() { - mset := visit.prog.MethodSets.MethodSet(T) - for i, n := 0, mset.Len(); i < n; i++ { - visit.function(visit.prog.MethodValue(mset.At(i))) + + // Historically, Program.RuntimeTypes used to include the type + // of any exported member of a package loaded from syntax that + // has a non-parameterized type, plus all types + // reachable from that type using reflection, even though + // these runtime types may not be required for them. + // + // Rather than break existing programs that rely on + // AllFunctions visiting extra methods that are unreferenced + // by IR and unreachable via reflection, we moved the logic + // here, unprincipled though it is. + // (See doc comment for better ideas.) + // + // Nonetheless, after the move, we no longer visit every + // method of any type recursively reachable from T, only the + // methods of T and *T themselves, and we only apply this to + // named types T, and not to the type of every exported + // package member. + exportedTypeHack := func(t *ir.Type) { + if isSyntactic(t.Package()) && + ast.IsExported(t.Name()) && + !types.IsInterface(t.Type()) { + // Consider only named types. + // (Ignore aliases and unsafe.Pointer.) + if named, ok := t.Type().(*types.Named); ok { + // Skip generic types. + if named.TypeParams() == nil { + methodsOf(named) // T + methodsOf(types.NewPointer(named)) // *T + } + } } } -} -func (visit *visitor) function(fn *ir.Function) { - if !visit.seen[fn] { - visit.seen[fn] = true - var buf [10]*ir.Value // avoid alloc in common case - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - for _, op := range instr.Operands(buf[:0]) { - if fn, ok := (*op).(*ir.Function); ok { - visit.function(fn) - } - } + for _, pkg := range prog.AllPackages() { + for _, mem := range pkg.Members { + switch mem := mem.(type) { + case *ir.Function: + // Visit all package-level declared functions. + // + // (This may include generic functions, which is + // inconsistent with the treatment of methods: + // we skip both generic methods, + // and methods of generic types.) + function(mem) + + case *ir.Type: + exportedTypeHack(mem) } } } + + // Visit all methods of types for which runtime types were + // materialized, as they are reachable through reflection. + for _, T := range prog.RuntimeTypes() { + methodsOf(T) + } + + return seen } // MainPackages returns the subset of the specified packages @@ -76,3 +155,12 @@ func MainPackages(pkgs []*ir.Package) []*ir.Package { } return mains } + +// TODO(adonovan): propose a principled API for this. One possibility +// is a new field, Package.SrcFunctions []*Function, which would +// contain the list of SrcFunctions described in point 2 of the +// AllFunctions doc comment, or nil if the package is not from syntax. +// But perhaps overloading nil vs empty slice is too subtle. +// +//go:linkname isSyntactic honnef.co/go/tools/go/ir.isSyntactic +func isSyntactic(pkg *ir.Package) bool diff --git a/vendor/honnef.co/go/tools/go/ir/lift.go b/vendor/honnef.co/go/tools/go/ir/lift.go index c2da8774..61324b0c 100644 --- a/vendor/honnef.co/go/tools/go/ir/lift.go +++ b/vendor/honnef.co/go/tools/go/ir/lift.go @@ -7,7 +7,7 @@ package ir // This file defines the lifting pass which tries to "lift" Alloc // cells (new/local variables) into SSA registers, replacing loads // with the dominating stored value, eliminating loads and stores, and -// inserting φ- and σ-nodes as needed. +// inserting φ-nodes as needed. // Cited papers and resources: // @@ -43,7 +43,6 @@ package ir // Also see many other "TODO: opt" suggestions in the code. import ( - "encoding/binary" "fmt" "os" "slices" @@ -79,16 +78,8 @@ func (df domFrontier) add(u, v *BasicBlock) { // the DF -> IDF step. func (df domFrontier) build(fn *Function) { for _, b := range fn.Blocks { - preds := b.Preds[0:len(b.Preds):len(b.Preds)] - if b == fn.Exit { - for i, v := range fn.fakeExits.values { - if v { - preds = append(preds, fn.Blocks[i]) - } - } - } - if len(preds) >= 2 { - for _, p := range preds { + if len(b.Preds) >= 2 { + for _, p := range b.Preds { runner := p for runner != b.dom.idom { df.add(runner, b) @@ -105,42 +96,27 @@ func buildDomFrontier(fn *Function) domFrontier { return df } -type postDomFrontier BlockMap[[]*BasicBlock] - -func (rdf postDomFrontier) add(u, v *BasicBlock) { - rdf[u.Index] = append(rdf[u.Index], v) -} - -func (rdf postDomFrontier) build(fn *Function) { - for _, b := range fn.Blocks { - succs := b.Succs[0:len(b.Succs):len(b.Succs)] - if fn.fakeExits.Has(b) { - succs = append(succs, fn.Exit) +func removeInstr(refs []Instruction, instr Instruction) []Instruction { + s := refs + i := -1 + for j := range s { + if instr == s[j] { + i = j + break } - if len(succs) >= 2 { - for _, s := range succs { - runner := s - for runner != b.pdom.idom { - rdf.add(runner, b) - runner = runner.pdom.idom - } - } + } + if i == -1 { + return s + } + // Don't start copying elements until we find one to delete. + for j := i + 1; j < len(s); j++ { + if v := s[j]; v != instr { + s[i] = v + i++ } } -} - -func buildPostDomFrontier(fn *Function) postDomFrontier { - rdf := make(postDomFrontier, len(fn.Blocks)) - rdf.build(fn) - return rdf -} - -func removeInstr(refs []Instruction, instr Instruction) []Instruction { - return removeInstrsIf(refs, func(i Instruction) bool { return i == instr }) -} - -func removeInstrsIf(refs []Instruction, p func(Instruction) bool) []Instruction { - return slices.DeleteFunc(refs, p) + clear(s[i:]) // zero/nil out the obsolete elements, for GC + return s[:i] } func clearInstrs(instrs []Instruction) { @@ -163,8 +139,8 @@ func numberNodesPerBlock(f *Function) { } // lift replaces local and new Allocs accessed only with -// load/store by IR registers, inserting φ- and σ-nodes where necessary. -// The result is a program in pruned SSI form. +// load/store by IR registers, inserting φ-nodes where necessary. +// The result is a program in pruned SSA form. // // Preconditions: // - fn has no dead blocks (blockopt has run). @@ -190,10 +166,7 @@ func lift(fn *Function) bool { // // But we will start with the simplest correct code. var df domFrontier - var rdf postDomFrontier - var closure *closure var newPhis BlockMap[[]newPhi] - var newSigmas BlockMap[[]newSigma] // During this pass we will replace some BasicBlock.Instrs // (allocs, loads and stores) with nil, keeping a count in @@ -221,6 +194,19 @@ func lift(fn *Function) bool { // Number nodes, for liftable numberNodesPerBlock(fn) + heads := make(BlockMap[int], len(fn.Blocks)) + for i := range heads { + heads[i] = -1 + } + for _, b := range fn.Blocks { + for i, instr := range b.Instrs { + if _, ok := instr.(*Phi); !ok { + heads[b.Index] = i + break + } + } + } + for _, b := range fn.Blocks { b.gaps = 0 b.rundefers = 0 @@ -228,19 +214,14 @@ func lift(fn *Function) bool { for _, instr := range b.Instrs { switch instr := instr.(type) { case *Alloc: - if !liftable(instr, instructions) { + if !liftable(instr, instructions, heads) { instr.index = -1 continue } if numAllocs == 0 { df = buildDomFrontier(fn) - rdf = buildPostDomFrontier(fn) - if len(fn.Blocks) > 2 { - closure = transitiveClosure(fn) - } newPhis = make(BlockMap[[]newPhi], len(fn.Blocks)) - newSigmas = make(BlockMap[[]newSigma], len(fn.Blocks)) if debugLifting { title := false @@ -261,11 +242,11 @@ func lift(fn *Function) bool { usesDefer = true if eliminateDeferStack { // Clear _DeferStack and remove references to loads - if instr._DeferStack != nil { - if refs := instr._DeferStack.Referrers(); refs != nil { + if instr.DeferStack != nil { + if refs := instr.DeferStack.Referrers(); refs != nil { *refs = removeInstr(*refs, instr) } - instr._DeferStack = nil + instr.DeferStack = nil } } case *RunDefers: @@ -303,7 +284,7 @@ func lift(fn *Function) bool { for _, b := range fn.Blocks { for _, instr := range b.Instrs { if instr, ok := instr.(*Alloc); ok && instr.index >= 0 { - liftAlloc(closure, df, rdf, instr, newPhis, newSigmas) + liftAlloc(df, instr, newPhis) } } } @@ -316,12 +297,12 @@ func lift(fn *Function) bool { renaming := make([]Value, numAllocs) // Renaming. - rename(fn.Blocks[0], renaming, newPhis, newSigmas) + rename(fn.Blocks[0], renaming, newPhis) - simplifyPhisAndSigmas(newPhis, newSigmas) + simplifyPhis(newPhis) - // Eliminate dead φ- and σ-nodes. - markLiveNodes(fn.Blocks, newPhis, newSigmas) + // Eliminate dead φ-nodes. + markLiveNodes(fn.Blocks, newPhis) // Eliminate ssa:deferstack() call. if eliminateDeferStack { @@ -342,23 +323,6 @@ func lift(fn *Function) bool { if numAllocs > 0 { nps := newPhis[b.Index] head = make([]Instruction, 0, len(nps)) - for _, pred := range b.Preds { - nss := newSigmas[pred.Index] - idx := pred.succIndex(b) - for _, newSigma := range nss { - if sigma := newSigma.sigmas[idx]; sigma != nil && sigma.live { - head = append(head, sigma) - - // we didn't populate referrers before, as most - // sigma nodes will be killed - if refs := sigma.X.Referrers(); refs != nil { - *refs = append(*refs, sigma) - } - } else if sigma != nil { - sigma.block = nil - } - } - } for _, np := range nps { if np.phi.live { head = append(head, np.phi) @@ -388,7 +352,7 @@ func lift(fn *Function) bool { // that seems to only be the case ~1% of the time, which // doesn't seem worth the extra branch. - // Remove dead instructions, add phis and sigmas + // Remove dead instructions, add phis ns := len(b.Instrs) + j - b.gaps - rundefersToKill if ns <= cap(b.Instrs) { // b.Instrs has enough capacity to store all instructions @@ -458,21 +422,17 @@ func lift(fn *Function) bool { func hasDirectReferrer(instr Instruction) bool { for _, instr := range *instr.Referrers() { - switch instr.(type) { - case *Phi, *Sigma: - // ignore - default: + if _, ok := instr.(*Phi); !ok { return true } } return false } -func markLiveNodes(blocks []*BasicBlock, newPhis BlockMap[[]newPhi], newSigmas BlockMap[[]newSigma]) { - // Phis and sigmas may become dead due to optimization passes. We may also insert more nodes than strictly - // necessary, e.g. sigma nodes for constants, which will never be used. +func markLiveNodes(blocks []*BasicBlock, newPhis BlockMap[[]newPhi]) { + // Phis may become dead due to optimization passes. - // Phi and sigma nodes are considered live if a non-phi, non-sigma + // Phi nodes are considered live if a non-phi // node uses them. Once we find a node that is live, we mark all // of its operands as used, too. for _, npList := range newPhis { @@ -483,15 +443,6 @@ func markLiveNodes(blocks []*BasicBlock, newPhis BlockMap[[]newPhi], newSigmas B } } } - for _, npList := range newSigmas { - for _, np := range npList { - for _, sigma := range np.sigmas { - if sigma != nil && !sigma.live && hasDirectReferrer(sigma) { - markLiveSigma(sigma) - } - } - } - } // Existing φ-nodes due to && and || operators // are all considered live (see Go issue 19622). for _, b := range blocks { @@ -504,48 +455,18 @@ func markLiveNodes(blocks []*BasicBlock, newPhis BlockMap[[]newPhi], newSigmas B func markLivePhi(phi *Phi) { phi.live = true for _, rand := range phi.Edges { - switch rand := rand.(type) { - case *Phi: + if rand, ok := rand.(*Phi); ok { if !rand.live { markLivePhi(rand) } - case *Sigma: - if !rand.live { - markLiveSigma(rand) - } } } } -func markLiveSigma(sigma *Sigma) { - sigma.live = true - switch rand := sigma.X.(type) { - case *Phi: - if !rand.live { - markLivePhi(rand) - } - case *Sigma: - if !rand.live { - markLiveSigma(rand) - } - } -} - -// simplifyPhisAndSigmas removes duplicate phi and sigma nodes, -// and replaces trivial phis with non-phi alternatives. Phi +// simplifyPhis replaces trivial phis with non-phi alternatives. Phi // nodes where all edges are identical, or consist of only the phi // itself and one other value, may be replaced with the value. -func simplifyPhisAndSigmas(newPhis BlockMap[[]newPhi], newSigmas BlockMap[[]newSigma]) { - // temporary numbering of values used in phis so that we can build map keys - var id ID - for _, npList := range newPhis { - for _, np := range npList { - for _, edge := range np.phi.Edges { - edge.setID(id) - id++ - } - } - } +func simplifyPhis(newPhis BlockMap[[]newPhi]) { // find all phis that are trivial and can be replaced with a // non-phi value. run until we reach a fixpoint, because replacing // a phi may make other phis trivial. @@ -554,7 +475,7 @@ func simplifyPhisAndSigmas(newPhis BlockMap[[]newPhi], newSigmas BlockMap[[]newS for _, npList := range newPhis { for _, np := range npList { if np.phi.live { - // we're reusing 'live' to mean 'dead' in the context of simplifyPhisAndSigmas + // we're reusing 'live' to mean 'dead' in the context of simplifyPhis continue } if r, ok := isUselessPhi(np.phi); ok { @@ -567,84 +488,6 @@ func simplifyPhisAndSigmas(newPhis BlockMap[[]newPhi], newSigmas BlockMap[[]newS } } } - - // Replace duplicate sigma nodes with a single node. These nodes exist when multiple allocs get replaced with the - // same dominating store. - for _, sigmaList := range newSigmas { - primarySigmas := map[struct { - succ int - v Value - }]*Sigma{} - for _, sigmas := range sigmaList { - for succ, sigma := range sigmas.sigmas { - if sigma == nil { - continue - } - if sigma.live { - // we're reusing 'live' to mean 'dead' in the context of simplifyPhisAndSigmas - continue - } - key := struct { - succ int - v Value - }{succ, sigma.X} - if alt, ok := primarySigmas[key]; ok { - replaceAll(sigma, alt) - sigma.live = true - changed = true - } else { - primarySigmas[key] = sigma - } - } - } - } - - // Replace duplicate phi nodes with a single node. As far as we know, these duplicate nodes only ever exist - // because of the previous sigma deduplication. - keyb := make([]byte, 0, 4*8) - for _, npList := range newPhis { - primaryPhis := map[string]*Phi{} - for _, np := range npList { - if np.phi.live { - continue - } - if n := len(np.phi.Edges) * 8; cap(keyb) >= n { - keyb = keyb[:n] - } else { - keyb = make([]byte, n, n*2) - } - for i, e := range np.phi.Edges { - binary.LittleEndian.PutUint64(keyb[i*8:i*8+8], uint64(e.ID())) - } - if alt, ok := primaryPhis[string(keyb)]; ok { - replaceAll(np.phi, alt) - np.phi.live = true - changed = true - } else { - primaryPhis[string(keyb)] = np.phi - } - } - } - - } - - for _, npList := range newPhis { - for _, np := range npList { - np.phi.live = false - for _, edge := range np.phi.Edges { - edge.setID(0) - } - } - } - - for _, sigmaList := range newSigmas { - for _, sigmas := range sigmaList { - for _, sigma := range sigmas.sigmas { - if sigma != nil { - sigma.live = false - } - } - } } } @@ -724,109 +567,6 @@ func (s *BlockSet) Take() int { return -1 } -type closure struct { - span []uint32 - reachables BlockMap[interval] -} - -type interval uint32 - -const ( - flagMask = 1 << 31 - numBits = 20 - lengthBits = 32 - numBits - 1 - lengthMask = (1<>numBits - } else { - // large interval - i++ - start = uint32(inv & numMask) - end = uint32(r[i]) - } - if idx >= start && idx <= end { - return true - } - } - return false -} - -func (c closure) reachable(id int) []interval { - return c.reachables[c.span[id]:c.span[id+1]] -} - -func (c closure) walk(current *BasicBlock, b *BasicBlock, visited []bool) { - // TODO(dh): the 'current' argument seems to be unused - // TODO(dh): there's no reason for this to be a method - visited[b.Index] = true - for _, succ := range b.Succs { - if visited[succ.Index] { - continue - } - visited[succ.Index] = true - c.walk(current, succ, visited) - } -} - -func transitiveClosure(fn *Function) *closure { - reachable := make(BlockMap[bool], len(fn.Blocks)) - c := &closure{} - c.span = make([]uint32, len(fn.Blocks)+1) - - addInterval := func(start, end uint32) { - if l := end - start; l <= 1<= 0 { // store of zero to Alloc cell - // Replace dominated loads by the zero value. - renaming[instr.index] = nil - if debugLifting { - fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr) - } - // Delete the Alloc. - u.Instrs[i] = nil - u.gaps++ - } + worklist := []worklistEntry{{u_, renaming_}} + var freelist [][]Value - case *Store: - if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell - // Replace dominated loads by the stored value. - renaming[alloc.index] = instr.Val - if debugLifting { - fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n", - instr, instr.Val.Name()) - } - if refs := instr.Addr.Referrers(); refs != nil { - *refs = removeInstr(*refs, instr) - } - if refs := instr.Val.Referrers(); refs != nil { - *refs = removeInstr(*refs, instr) - } - // Delete the Store. - u.Instrs[i] = nil - u.gaps++ - } + for len(worklist) > 0 { + entry := worklist[len(worklist)-1] + worklist = worklist[:len(worklist)-1] + u, renaming := entry.u, entry.renaming - case *Load: - if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell - // In theory, we wouldn't be able to replace loads directly, because a loaded value could be used in - // different branches, in which case it should be replaced with different sigma nodes. But we can't - // simply defer replacement, either, because then later stores might incorrectly affect this load. - // - // To avoid doing renaming on _all_ values (instead of just loads and stores like we're doing), we make - // sure during code generation that each load is only used in one block. For example, in constant switch - // statements, where the tag is only evaluated once, we store it in a temporary and load it for each - // comparison, so that we have individual loads to replace. - // - // Because we only rename stores and loads, the end result will not contain sigma nodes for all - // constants. Some constants may be used directly, e.g. in comparisons such as 'x == 5'. We may still - // end up inserting dead sigma nodes in branches, but these will never get used in renaming and will be - // cleaned up when we remove dead phis and sigmas. - newval := renamed(u.Parent(), renaming, alloc) - if debugLifting { - fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n", - instr.Name(), instr, newval) - } - replaceAll(instr, newval) - u.Instrs[i] = nil - u.gaps++ - } + // Each φ-node becomes the new name for its associated Alloc. + for _, np := range newPhis[u.Index] { + phi := np.phi + alloc := np.alloc + renaming[alloc.index] = phi + } - case *DebugRef: - if x, ok := instr.X.(*Alloc); ok && x.index >= 0 { - if instr.IsAddr { - instr.X = renamed(u.Parent(), renaming, x) - instr.IsAddr = false + // Rename loads and stores of allocs. + for i, instr := range u.Instrs { + switch instr := instr.(type) { + case *Alloc: + if instr.index >= 0 { // store of zero to Alloc cell + // Replace dominated loads by the zero value. + renaming[instr.index] = nil + if debugLifting { + fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr) + } + // Delete the Alloc. + u.Instrs[i] = nil + u.gaps++ + } - // Add DebugRef to instr.X's referrers. - if refs := instr.X.Referrers(); refs != nil { - *refs = append(*refs, instr) + case *Store: + if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell + // Replace dominated loads by the stored value. + renaming[alloc.index] = instr.Val + if debugLifting { + fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n", + instr, instr.Val.Name()) } - } else { - // A source expression denotes the address - // of an Alloc that was optimized away. - instr.X = nil + if refs := instr.Addr.Referrers(); refs != nil { + *refs = removeInstr(*refs, instr) + } + if refs := instr.Val.Referrers(); refs != nil { + *refs = removeInstr(*refs, instr) + } + // Delete the Store. + u.Instrs[i] = nil + u.gaps++ + } - // Delete the DebugRef. + case *Load: + if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell + newval := renamed(u.Parent(), renaming, alloc) + if debugLifting { + fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n", + instr.Name(), instr, newval) + } + // Replace all references to the loaded value by the dominating + // stored value. + replaceAll(instr, newval) u.Instrs[i] = nil u.gaps++ } + + case *debugRef: + if x, ok := instr.X.(*Alloc); ok && x.index >= 0 { + if instr.IsAddr { + instr.X = renamed(u.Parent(), renaming, x) + instr.IsAddr = false + + // Add DebugRef to instr.X's referrers. + if refs := instr.X.Referrers(); refs != nil { + *refs = append(*refs, instr) + } + } else { + // A source expression denotes the address + // of an Alloc that was optimized away. + instr.X = nil + + // Delete the DebugRef. + u.Instrs[i] = nil + u.gaps++ + } + } } } - } - // update all outgoing sigma nodes with the dominating store - for _, sigmas := range newSigmas[u.Index] { - for _, sigma := range sigmas.sigmas { - if sigma == nil { + // For each φ-node in a CFG successor, rename the edge. + for _, v := range u.Succs { + phis := newPhis[v.Index] + if len(phis) == 0 { continue } - sigma.X = renamed(u.Parent(), renaming, sigmas.alloc) - } - } - - // For each φ-node in a CFG successor, rename the edge. - for succi, v := range u.Succs { - phis := newPhis[v.Index] - if len(phis) == 0 { - continue - } - i := v.predIndex(u) - for _, np := range phis { - phi := np.phi - alloc := np.alloc - // if there's a sigma node, use it, else use the dominating value - var newval Value - for _, sigmas := range newSigmas[u.Index] { - if sigmas.alloc == alloc && sigmas.sigmas[succi] != nil { - newval = sigmas.sigmas[succi] - break + i := v.predIndex(u) + for _, np := range phis { + phi := np.phi + alloc := np.alloc + newval := renamed(u.Parent(), renaming, alloc) + if debugLifting { + fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n", + phi.Name(), u, v, i, alloc.Name(), newval.Name()) + } + phi.Edges[i] = newval + if prefs := newval.Referrers(); prefs != nil { + *prefs = append(*prefs, phi) } - } - if newval == nil { - newval = renamed(u.Parent(), renaming, alloc) - } - if debugLifting { - fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n", - phi.Name(), u, v, i, alloc.Name(), newval.Name()) - } - phi.Edges[i] = newval - if prefs := newval.Referrers(); prefs != nil { - *prefs = append(*prefs, phi) } } - } - // Continue depth-first recursion over domtree, pushing a - // fresh copy of the renaming map for each subtree. - r := make([]Value, len(renaming)) - for _, v := range u.dom.children { - copy(r, renaming) - - // on entry to a block, the incoming sigma nodes become the new values for their alloc - if idx := u.succIndex(v); idx != -1 { - for _, sigma := range newSigmas[u.Index] { - if sigma.sigmas[idx] != nil { - r[sigma.alloc.index] = sigma.sigmas[idx] - } + // Continue depth-first recursion over domtree, pushing a + // fresh copy of the renaming map for each subtree. + for _, v := range slices.Backward(u.dom.children) { + var r []Value + if len(freelist) == 0 { + r = make([]Value, len(renaming)) + } else { + r = freelist[len(freelist)-1] + freelist = freelist[:len(freelist)-1] } + copy(r, renaming) + worklist = append(worklist, worklistEntry{v, r}) } - rename(v, r, newPhis, newSigmas) + freelist = append(freelist, renaming) } - } func simplifyConstantCompositeValues(fn *Function) bool { @@ -1772,7 +1216,7 @@ func simplifyConstantCompositeValues(fn *Function) bool { } } if replaced { - replaceAll(cv, emitConst(fn, ac)) + replaceAll(cv, ac) killInstruction(cv) } diff --git a/vendor/honnef.co/go/tools/go/ir/lvalue.go b/vendor/honnef.co/go/tools/go/ir/lvalue.go index 86eb4a5d..502293c8 100644 --- a/vendor/honnef.co/go/tools/go/ir/lvalue.go +++ b/vendor/honnef.co/go/tools/go/ir/lvalue.go @@ -24,7 +24,7 @@ type lvalue interface { // An address is an lvalue represented by a true pointer. type address struct { - addr Value + addr Value // must have a pointer core type. expr ast.Expr // source syntax of the value (not address) [debug mode] } diff --git a/vendor/honnef.co/go/tools/go/ir/methods.go b/vendor/honnef.co/go/tools/go/ir/methods.go index 082029da..9ad8e410 100644 --- a/vendor/honnef.co/go/tools/go/ir/methods.go +++ b/vendor/honnef.co/go/tools/go/ir/methods.go @@ -10,235 +10,168 @@ import ( "fmt" "go/types" - "honnef.co/go/tools/analysis/lint" + "golang.org/x/tools/go/types/typeutil" + "honnef.co/go/tools/internal/xtools-internal/typesinternal" ) // MethodValue returns the Function implementing method sel, building -// wrapper methods on demand. It returns nil if sel denotes an -// abstract (interface) method. +// wrapper methods on demand. It returns nil if sel denotes an +// interface or generic method. // // Precondition: sel.Kind() == MethodVal. // // Thread-safe. // -// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) +// Acquires prog.methodsMu. func (prog *Program) MethodValue(sel *types.Selection) *Function { if sel.Kind() != types.MethodVal { panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel)) } T := sel.Recv() if types.IsInterface(T) { - return nil // abstract method + return nil // interface method or type parameter } + + if prog.isParameterized(T, sel.Type()) { + return nil // method on generic type or generic method + } + if prog.mode&LogSource != 0 { defer logStack("MethodValue %s %v", T, sel)() } - prog.methodsMu.Lock() - defer prog.methodsMu.Unlock() - - return prog.addMethod(prog.createMethodSet(T), sel) -} - -// LookupMethod returns the implementation of the method of type T -// identified by (pkg, name). It returns nil if the method exists but -// is abstract, and panics if T has no such method. -func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function { - sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name) - if sel == nil { - panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name))) - } - return prog.MethodValue(sel) -} + var b builder -// methodSet contains the (concrete) methods of a non-interface type. -type methodSet struct { - mapping map[string]*Function // populated lazily - complete bool // mapping contains all methods -} + m := func() *Function { + prog.methodsMu.Lock() + defer prog.methodsMu.Unlock() -// Precondition: !isInterface(T). -// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) -func (prog *Program) createMethodSet(T types.Type) *methodSet { - mset, ok := prog.methodSets.At(T) - if !ok { - mset = &methodSet{mapping: make(map[string]*Function)} - prog.methodSets.Set(T, mset) - } - return mset -} + // Get or create SSA method set. + mset, ok := prog.methodSets.At(T) + if !ok { + mset = &methodSet{mapping: make(map[string]*Function)} + prog.methodSets.Set(T, mset) + } -// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) -func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function { - if sel.Kind() == types.MethodExpr { - panic(sel) - } - id := sel.Obj().Id() - fn := mset.mapping[id] - if fn == nil { - obj := sel.Obj().(*types.Func) - - needsPromotion := len(sel.Index()) > 1 - needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv()) - if needsPromotion || needsIndirection { - fn = makeWrapper(prog, sel) + // Get or create SSA method. + id := sel.Obj().Id() + fn, ok := mset.mapping[id] + if !ok { + obj := sel.Obj().(*types.Func) + needsPromotion := len(sel.Index()) > 1 + needsIndirection := !isPointer(recvType(obj)) && isPointer(T) + if needsPromotion || needsIndirection { + fn = createWrapper(prog, toSelection(sel), nil) + fn.buildshared = b.shared() + b.enqueue(fn) + } else { + fn = prog.objectMethod(obj, nil, &b) + } + if fn.Signature.Recv() == nil { + panic(fn) + } + mset.mapping[id] = fn } else { - fn = prog.declaredFunc(obj) - } - if fn.Signature.Recv() == nil { - panic(fn) // missing receiver + b.waitForSharedFunction(fn) } - mset.mapping[id] = fn - } - return fn -} -// RuntimeTypes returns a new unordered slice containing all -// concrete types in the program for which a complete (non-empty) -// method set is required at run-time. -// -// Thread-safe. -// -// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) -func (prog *Program) RuntimeTypes() []types.Type { - prog.methodsMu.Lock() - defer prog.methodsMu.Unlock() + return fn + }() - var res []types.Type - prog.methodSets.Iterate(func(T types.Type, v *methodSet) { - if v.complete { - res = append(res, T) - } - }) - return res -} + b.iterate() -// declaredFunc returns the concrete function/method denoted by obj. -// Panic ensues if there is none. -func (prog *Program) declaredFunc(obj *types.Func) *Function { - if origin := obj.Origin(); origin != obj { - // Calling method on instantiated type, create a wrapper that calls the generic type's method - base := prog.packageLevelValue(origin) - return makeInstance(prog, base.(*Function), obj.Type().(*types.Signature), nil) - } else { - if v := prog.packageLevelValue(obj); v != nil { - return v.(*Function) - } - } - panic("no concrete method: " + obj.String()) + return m } -// needMethodsOf ensures that runtime type information (including the -// complete method set) is available for the specified type T and all -// its subcomponents. -// -// needMethodsOf must be called for at least every type that is an -// operand of some MakeInterface instruction, and for the type of -// every exported package member. -// -// Precondition: T is not a method signature (*Signature with Recv()!=nil). +// objectMethod returns the Function for a given method symbol. +// The symbol may be an instance of a generic function. It need not +// belong to an existing SSA package created by a call to +// prog.CreatePackage. // -// Thread-safe. (Called via emitConv from multiple builder goroutines.) +// objectMethod panics if the function is not a method. // -// TODO(adonovan): make this faster. It accounts for 20% of SSA build time. -// -// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) -func (prog *Program) needMethodsOf(T types.Type) { - prog.methodsMu.Lock() - prog.needMethods(T, false) - prog.methodsMu.Unlock() -} - -// Precondition: T is not a method signature (*Signature with Recv()!=nil). -// Recursive case: skip => don't create methods for T. -// -// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) -func (prog *Program) needMethods(T types.Type, skip bool) { - // Each package maintains its own set of types it has visited. - if prevSkip, ok := prog.runtimeTypes.At(T); ok { - // needMethods(T) was previously called - if !prevSkip || skip { - return // already seen, with same or false 'skip' value - } - } - prog.runtimeTypes.Set(T, skip) - - tmset := prog.MethodSets.MethodSet(T) - - if !skip && !types.IsInterface(T) && tmset.Len() > 0 { - // Create methods of T. - mset := prog.createMethodSet(T) - if !mset.complete { - mset.complete = true - n := tmset.Len() - for i := range n { - prog.addMethod(mset, tmset.At(i)) - } - } +// Acquires prog.objectMethodsMu. +func (prog *Program) objectMethod(obj *types.Func, targs []types.Type, b *builder) *Function { + sig := obj.Type().(*types.Signature) + if sig.Recv() == nil { + panic("not a method: " + obj.String()) } - // Recursion over signatures of each method. - for method := range tmset.Methods() { - sig := method.Type().(*types.Signature) - prog.needMethods(sig.Params(), false) - prog.needMethods(sig.Results(), false) + // Instantiation of generic? + if orig := obj.Origin(); orig != obj || len(targs) > 0 { + return prog.objectMethod(orig, nil, b).instance(receiverTypeArgs(obj), targs, b) } - switch t := T.(type) { - case *types.Basic: - // nop - - case *types.Interface, *types.TypeParam: - // nop---handled by recursion over method set. - - case *types.Pointer: - prog.needMethods(t.Elem(), false) - - case *types.Slice: - prog.needMethods(t.Elem(), false) - - case *types.Chan: - prog.needMethods(t.Elem(), false) + // Belongs to a created package? + if fn := prog.FuncValue(obj); fn != nil { + return fn + } - case *types.Map: - prog.needMethods(t.Key(), false) - prog.needMethods(t.Elem(), false) + // Consult/update cache of methods created from types.Func. + prog.objectMethodsMu.Lock() + defer prog.objectMethodsMu.Unlock() + fn, ok := prog.objectMethods[obj] + if !ok { + fn = createFunction(prog, obj, obj.Name(), nil, nil, "") + fn.Synthetic = "from type information (on demand)" + fn.buildshared = b.shared() + b.enqueue(fn) - case *types.Signature: - if t.Recv() != nil { - panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv())) - } - prog.needMethods(t.Params(), false) - prog.needMethods(t.Results(), false) - - case *types.Named: - // A pointer-to-named type can be derived from a named - // type via reflection. It may have methods too. - prog.needMethods(types.NewPointer(t), false) - - // Consider 'type T struct{S}' where S has methods. - // Reflection provides no way to get from T to struct{S}, - // only to S, so the method set of struct{S} is unwanted, - // so set 'skip' flag during recursion. - prog.needMethods(t.Underlying(), true) - - case *types.Array: - prog.needMethods(t.Elem(), false) - - case *types.Struct: - for i, n := 0, t.NumFields(); i < n; i++ { - prog.needMethods(t.Field(i).Type(), false) + if prog.objectMethods == nil { + prog.objectMethods = make(map[*types.Func]*Function) } + prog.objectMethods[obj] = fn + } else { + b.waitForSharedFunction(fn) + } + return fn +} - case *types.Tuple: - for i, n := 0, t.Len(); i < n; i++ { - prog.needMethods(t.At(i).Type(), false) - } +// LookupMethod returns the implementation of the method of type T +// identified by (pkg, name). It returns nil if the method exists but +// is an interface method or generic method, and panics if T has no such method. +func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function { + sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name) + if sel == nil { + panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name))) + } + return prog.MethodValue(sel) +} - case *types.Alias: - prog.needMethods(types.Unalias(t), false) +// methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized). +type methodSet struct { + mapping map[string]*Function // populated lazily +} - default: - lint.ExhaustiveTypeSwitch(T) +// RuntimeTypes returns a new unordered slice containing all types in +// the program for which a runtime type is required. +// +// A runtime type is required for any non-parameterized, non-interface +// type that is converted to an interface, or for any type (including +// interface types) derivable from one through reflection. +// +// The methods of such types may be reachable through reflection or +// interface calls even if they are never called directly. +// +// Thread-safe. +// +// Acquires prog.makeInterfaceTypesMu. +func (prog *Program) RuntimeTypes() []types.Type { + prog.makeInterfaceTypesMu.Lock() + defer prog.makeInterfaceTypesMu.Unlock() + + // Compute the derived types on demand, since many SSA clients + // never call RuntimeTypes, and those that do typically call + // it once (often within irutil.AllFunctions, which will + // eventually not use it; see Go issue #69291.) This + // eliminates the need to eagerly compute all the element + // types during SSA building. + var runtimeTypes []types.Type + add := func(t types.Type) { runtimeTypes = append(runtimeTypes, t) } + var set typeutil.Map // for de-duping identical types + for t := range prog.makeInterfaceTypes { + typesinternal.ForEachElement(&set, &prog.MethodSets, t, add) } + + return runtimeTypes } diff --git a/vendor/honnef.co/go/tools/go/ir/mode.go b/vendor/honnef.co/go/tools/go/ir/mode.go index 8a87a605..8f55bce0 100644 --- a/vendor/honnef.co/go/tools/go/ir/mode.go +++ b/vendor/honnef.co/go/tools/go/ir/mode.go @@ -20,26 +20,30 @@ import ( type BuilderMode uint const ( - PrintPackages BuilderMode = 1 << iota // Print package inventory to stdout - PrintFunctions // Print function IR code to stdout - PrintSource // Print source code when printing function IR - LogSource // Log source locations as IR builder progresses - SanityCheckFunctions // Perform sanity checking of function bodies - NaiveForm // Build naïve IR form: don't replace local loads/stores with registers - GlobalDebug // Enable debug info for all packages - SplitAfterNewInformation // Split live range after we learn something new about a value + PrintPackages BuilderMode = 1 << iota // Print package inventory to stdout + PrintFunctions // Print function IR code to stdout + PrintSource // Print source code when printing function IR + LogSource // Log source locations as IR builder progresses + SanityCheckFunctions // Perform sanity checking of function bodies + NaiveForm // Build naïve IR form: don't replace local loads/stores with registers + BuildSerially // Build packages serially, not in parallel. + GlobalDebug // Enable debug info for all packages + BareInits // Build init functions without guards or calls to dependent inits + InstantiateGenerics // Instantiate generics functions (monomorphize) while building ) const BuilderModeDoc = `Options controlling the IR builder. -The value is a sequence of zero or more of these symbols: +The value is a sequence of zero or more of these letters: C perform sanity [C]hecking of the IR form. D include [D]ebug info for every function. P print [P]ackage inventory. F print [F]unction IR code. A print [A]ST nodes responsible for IR instructions S log [S]ource locations as IR builder progresses. +L build distinct packages seria[L]ly instead of in parallel. N build [N]aive IR form: don't replace local loads/stores with registers. -I Split live range after a value is used as slice or array index +I build bare [I]nit functions: no init guards or calls to dependent inits. +G instantiate [G]eneric function bodies via monomorphization ` func (m BuilderMode) String() string { @@ -65,9 +69,15 @@ func (m BuilderMode) String() string { if m&NaiveForm != 0 { buf.WriteByte('N') } - if m&SplitAfterNewInformation != 0 { + if m&BuildSerially != 0 { + buf.WriteByte('L') + } + if m&BareInits != 0 { buf.WriteByte('I') } + if m&InstantiateGenerics != 0 { + buf.WriteByte('G') + } return buf.String() } @@ -85,13 +95,17 @@ func (m *BuilderMode) Set(s string) error { case 'A': mode |= PrintSource case 'S': - mode |= LogSource + mode |= LogSource | BuildSerially case 'C': mode |= SanityCheckFunctions case 'N': mode |= NaiveForm + case 'L': + mode |= BuildSerially case 'I': - mode |= SplitAfterNewInformation + mode |= BareInits + case 'G': + mode |= InstantiateGenerics default: return fmt.Errorf("unknown BuilderMode option: %q", c) } diff --git a/vendor/honnef.co/go/tools/go/ir/print.go b/vendor/honnef.co/go/tools/go/ir/print.go index f2468763..c71518ad 100644 --- a/vendor/honnef.co/go/tools/go/ir/print.go +++ b/vendor/honnef.co/go/tools/go/ir/print.go @@ -24,17 +24,19 @@ import ( // Functions (including methods) and Globals use RelString and // all types are displayed with relType, so that only cross-package // references are package-qualified. -func relName(v Value, i Instruction) string { +func relName(v Value, i Node) string { if v == nil { return "" } var from *types.Package if i != nil { - from = i.Parent().pkg() + from = i.Parent().relPkg() } switch v := v.(type) { case Member: // *Function or *Global return v.RelString(from) + case *Const: + return v.RelString(from) } return v.Name() } @@ -66,41 +68,37 @@ func relString(m Member, from *types.Package) string { // It never appears in disassembly, which uses Value.Name(). func (v *Parameter) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("Parameter <%s> {%s}", relType(v.Type(), from), v.name) + from := v.Parent().relPkg() + return fmt.Sprintf("parameter %s : %s", v.Object().Name(), relType(v.Type(), from)) } func (v *FreeVar) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("FreeVar <%s> %s", relType(v.Type(), from), v.Name()) + from := v.Parent().relPkg() + return fmt.Sprintf("freevar %s : %s", v.Name(), relType(v.Type(), from)) } func (v *Builtin) String() string { - return fmt.Sprintf("Builtin %s", v.Name()) + return fmt.Sprintf("builtin %s", v.Name()) } // Instruction.String() func (v *Alloc) String() string { - from := v.Parent().pkg() - storage := "Stack" + op := "local" if v.Heap { - storage = "Heap" + op = "new" } - return fmt.Sprintf("%sAlloc <%s>", storage, relType(v.Type(), from)) -} - -func (v *Sigma) String() string { - from := v.Parent().pkg() - s := fmt.Sprintf("Sigma <%s> [b%d] %s", relType(v.Type(), from), v.From.Index, v.X.Name()) - return s + from := v.Parent().relPkg() + return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), from), v.Comment()) } func (v *Phi) String() string { var b bytes.Buffer - fmt.Fprintf(&b, "Phi <%s>", v.Type()) + b.WriteString("phi [") for i, edge := range v.Edges { - b.WriteString(" ") + if i > 0 { + b.WriteString(", ") + } // Be robust against malformed CFG. if v.block == nil { b.WriteString("??") @@ -110,39 +108,40 @@ func (v *Phi) String() string { if i < len(v.block.Preds) { block = v.block.Preds[i].Index } - fmt.Fprintf(&b, "%d:", block) + fmt.Fprintf(&b, "%d: ", block) edgeVal := "" // be robust if edge != nil { edgeVal = relName(edge, v) } b.WriteString(edgeVal) } + b.WriteString("]") + if v.Comment() != "" { + b.WriteString(" #") + b.WriteString(v.Comment()) + } return b.String() } func printCall(v *CallCommon, prefix string, instr Instruction) string { var b bytes.Buffer + b.WriteString(prefix) if !v.IsInvoke() { - if value, ok := instr.(Value); ok { - fmt.Fprintf(&b, "%s <%s> %s", prefix, relType(value.Type(), instr.Parent().pkg()), relName(v.Value, instr)) - } else { - fmt.Fprintf(&b, "%s %s", prefix, relName(v.Value, instr)) - } + b.WriteString(relName(v.Value, instr)) } else { - if value, ok := instr.(Value); ok { - fmt.Fprintf(&b, "%sInvoke <%s> %s.%s", prefix, relType(value.Type(), instr.Parent().pkg()), relName(v.Value, instr), v.Method.Name()) - } else { - fmt.Fprintf(&b, "%sInvoke %s.%s", prefix, relName(v.Value, instr), v.Method.Name()) - } + fmt.Fprintf(&b, "invoke %s.%s", relName(v.Value, instr), v.Method.Name()) } - for _, arg := range v.TypeArgs { - b.WriteString(" ") - b.WriteString(relType(arg, instr.Parent().pkg())) - } - for _, arg := range v.Args { - b.WriteString(" ") + b.WriteString("(") + for i, arg := range v.Args { + if i > 0 { + b.WriteString(", ") + } b.WriteString(relName(arg, instr)) } + if v.Signature().Variadic() { + b.WriteString("...") + } + b.WriteString(")") return b.String() } @@ -151,65 +150,97 @@ func (c *CallCommon) String() string { } func (v *Call) String() string { - return printCall(&v.Call, "Call", v) + return printCall(&v.Call, "", v) } func (v *BinOp) String() string { - return fmt.Sprintf("BinOp <%s> {%s} %s %s", relType(v.Type(), v.Parent().pkg()), v.Op.String(), relName(v.X, v), relName(v.Y, v)) + return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v)) } func (v *UnOp) String() string { - return fmt.Sprintf("UnOp <%s> {%s} %s", relType(v.Type(), v.Parent().pkg()), v.Op.String(), relName(v.X, v)) + return fmt.Sprintf("%s%s", v.Op, relName(v.X, v)) } func (v *Load) String() string { - return fmt.Sprintf("Load <%s> %s", relType(v.Type(), v.Parent().pkg()), relName(v.X, v)) -} - -func (v *Copy) String() string { - return fmt.Sprintf("Copy <%s> %s", relType(v.Type(), v.Parent().pkg()), relName(v.X, v)) + return fmt.Sprintf("*%s", relName(v.X, v)) } func printConv(prefix string, v, x Value) string { - from := v.Parent().pkg() - return fmt.Sprintf("%s <%s> %s", + from := v.Parent().relPkg() + return fmt.Sprintf("%s %s <- %s (%s)", prefix, relType(v.Type(), from), + relType(x.Type(), from), relName(x, v.(Instruction))) } -func (v *ChangeType) String() string { return printConv("ChangeType", v, v.X) } -func (v *Convert) String() string { return printConv("Convert", v, v.X) } -func (v *ChangeInterface) String() string { return printConv("ChangeInterface", v, v.X) } -func (v *SliceToArrayPointer) String() string { return printConv("SliceToArrayPointer", v, v.X) } +func (v *ChangeType) String() string { return printConv("changetype", v, v.X) } +func (v *Convert) String() string { return printConv("convert", v, v.X) } +func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) } +func (v *SliceToArrayPointer) String() string { return printConv("slice to array pointer", v, v.X) } func (v *SliceToArray) String() string { return printConv("SliceToArray", v, v.X) } -func (v *MakeInterface) String() string { return printConv("MakeInterface", v, v.X) } +func (v *MakeInterface) String() string { return printConv("make", v, v.X) } + +func (v *MultiConvert) String() string { + from := v.Parent().relPkg() + + var b strings.Builder + b.WriteString(printConv("multiconvert", v, v.X)) + b.WriteString(" [") + for i, s := range termListOf(v.from) { + for j, d := range termListOf(v.to) { + if i != 0 || j != 0 { + b.WriteString(" | ") + } + fmt.Fprintf(&b, "%s <- %s", relTerm(d, from), relTerm(s, from)) + } + } + b.WriteString("]") + return b.String() +} func (v *MakeClosure) String() string { - from := v.Parent().pkg() var b bytes.Buffer - fmt.Fprintf(&b, "MakeClosure <%s> %s", relType(v.Type(), from), relName(v.Fn, v)) + fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v)) if v.Bindings != nil { - for _, c := range v.Bindings { - b.WriteString(" ") + b.WriteString(" [") + for i, c := range v.Bindings { + if i > 0 { + b.WriteString(", ") + } b.WriteString(relName(c, v)) } + b.WriteString("]") } return b.String() } func (v *MakeSlice) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("MakeSlice <%s> %s %s", + from := v.Parent().relPkg() + return fmt.Sprintf("make %s %s %s", relType(v.Type(), from), relName(v.Len, v), relName(v.Cap, v)) } func (v *Slice) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("Slice <%s> %s %s %s %s", - relType(v.Type(), from), relName(v.X, v), relName(v.Low, v), relName(v.High, v), relName(v.Max, v)) + var b bytes.Buffer + b.WriteString("slice ") + b.WriteString(relName(v.X, v)) + b.WriteString("[") + if v.Low != nil { + b.WriteString(relName(v.Low, v)) + } + b.WriteString(":") + if v.High != nil { + b.WriteString(relName(v.High, v)) + } + if v.Max != nil { + b.WriteString(":") + b.WriteString(relName(v.Max, v)) + } + b.WriteString("]") + return b.String() } func (v *MakeMap) String() string { @@ -217,77 +248,69 @@ func (v *MakeMap) String() string { if v.Reserve != nil { res = relName(v.Reserve, v) } - from := v.Parent().pkg() - return fmt.Sprintf("MakeMap <%s> %s", relType(v.Type(), from), res) + from := v.Parent().relPkg() + return fmt.Sprintf("make %s %s", relType(v.Type(), from), res) } func (v *MakeChan) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("MakeChan <%s> %s", relType(v.Type(), from), relName(v.Size, v)) + from := v.Parent().relPkg() + return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v)) } func (v *FieldAddr) String() string { - from := v.Parent().pkg() - // v.X.Type() might be a pointer to a type parameter whose core type is a pointer to a struct - st := deref(typeutil.CoreType(deref(v.X.Type()))).Underlying().(*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(deref(v.X.Type()), v.Field); fld != nil { + name = fld.Name() } - return fmt.Sprintf("FieldAddr <%s> [%d] (%s) %s", relType(v.Type(), from), v.Field, name, relName(v.X, v)) + return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field) } func (v *Field) String() string { - st := typeutil.CoreType(v.X.Type()).Underlying().(*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() } - from := v.Parent().pkg() - return fmt.Sprintf("Field <%s> [%d] (%s) %s", relType(v.Type(), from), v.Field, name, relName(v.X, v)) + return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field) } func (v *IndexAddr) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("IndexAddr <%s> %s %s", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v)) + return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v)) } func (v *Index) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("Index <%s> %s %s", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v)) + return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v)) } func (v *MapLookup) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("MapLookup <%s> %s %s", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v)) + return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk)) } func (v *StringLookup) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("StringLookup <%s> %s %s", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v)) + return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v)) } func (v *Range) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("Range <%s> %s", relType(v.Type(), from), relName(v.X, v)) + return "range " + relName(v.X, v) } func (v *Next) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("Next <%s> %s", relType(v.Type(), from), relName(v.Iter, v)) + return "next " + relName(v.Iter, v) } func (v *TypeAssert) String() string { - from := v.Parent().pkg() - return fmt.Sprintf("TypeAssert <%s> %s", relType(v.Type(), from), relName(v.X, v)) + from := v.Parent().relPkg() + return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from)) } func (v *Extract) String() string { - from := v.Parent().pkg() name := v.Tuple.Type().(*types.Tuple).At(v.Index).Name() - return fmt.Sprintf("Extract <%s> [%d] (%s) %s", relType(v.Type(), from), v.Index, name, relName(v.Tuple, v)) + if name == "" { + return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index) + } else { + return fmt.Sprintf("extract %s #%d (%s)", relName(v.Tuple, v), v.Index, name) + } } func (s *Jump) String() string { @@ -296,7 +319,7 @@ func (s *Jump) String() string { if s.block != nil && len(s.block.Succs) == 1 { block = s.block.Succs[0].Index } - str := fmt.Sprintf("Jump → b%d", block) + str := fmt.Sprintf("jump %d", block) if s.Comment() != "" { str = fmt.Sprintf("%s # %s", str, s.Comment()) } @@ -304,12 +327,7 @@ func (s *Jump) String() string { } func (s *Unreachable) String() string { - // Be robust against malformed CFG. - block := -1 - if s.block != nil && len(s.block.Succs) == 1 { - block = s.block.Succs[0].Index - } - return fmt.Sprintf("Unreachable → b%d", block) + return "Unreachable" } func (s *If) String() string { @@ -319,7 +337,7 @@ func (s *If) String() string { tblock = s.block.Succs[0].Index fblock = s.block.Succs[1].Index } - return fmt.Sprintf("If %s → b%d b%d", relName(s.Cond, s), tblock, fblock) + return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock) } func (s *ConstantSwitch) String() string { @@ -337,79 +355,89 @@ func (s *ConstantSwitch) String() string { func (v *CompositeValue) String() string { var b bytes.Buffer - from := v.Parent().pkg() - fmt.Fprintf(&b, "CompositeValue <%s>", relType(v.Type(), from)) - if v.NumSet >= len(v.Values) { - // All values provided - fmt.Fprint(&b, " [all]") - } else if v.Bitmap.BitLen() == 0 { - // No values provided - fmt.Fprint(&b, " [none]") - } else { - // Some values provided - bits := fmt.Appendf(nil, "%0*b", len(v.Values), &v.Bitmap) - for i := 0; i < len(bits)/2; i++ { - o := len(bits) - 1 - i - bits[i], bits[o] = bits[o], bits[i] + fmt.Fprint(&b, "{") + first := true + + var printValue func(idx int, vv Value) + switch typ := typeutil.CoreType(v.typ).(type) { + case *types.Struct: + printValue = func(idx int, vv Value) { + fieldName := typ.Field(idx).Name() + fmt.Fprintf(&b, "%s: %s", fieldName, relName(vv, v)) } - fmt.Fprintf(&b, " [%s]", bits) + case *types.Array: + if v.NumSet >= len(v.Values) { + printValue = func(idx int, vv Value) { + fmt.Fprint(&b, relName(vv, v)) + } + } else { + // This is currently unreachable as we don't use CompositeValue for + // incomplete arrays. + printValue = func(idx int, vv Value) { + fmt.Fprintf(&b, "%d: %s", idx, relName(vv, v)) + } + } + default: + panic(fmt.Sprintf("internal error: unexpected type %T (%v)", typ, typ)) } - for _, vv := range v.Values { - fmt.Fprintf(&b, " %s", relName(vv, v)) + for i, vv := range v.Values { + if !first { + fmt.Fprint(&b, ", ") + } + first = false + printValue(i, vv) } + fmt.Fprint(&b, "}") return b.String() } func (s *TypeSwitch) String() string { - from := s.Parent().pkg() var b bytes.Buffer - fmt.Fprintf(&b, "TypeSwitch <%s> %s", relType(s.typ, from), relName(s.Tag, s)) + fmt.Fprintf(&b, "switch %s.(type)", relName(s.Tag, s)) for _, cond := range s.Conds { - fmt.Fprintf(&b, " %q", relType(cond, s.block.parent.pkg())) + fmt.Fprintf(&b, " %s", relType(cond, s.block.parent.relPkg())) } return b.String() } func (s *Go) String() string { - return printCall(&s.Call, "Go", s) + return printCall(&s.Call, "go ", s) } func (s *Panic) String() string { - // Be robust against malformed CFG. - block := -1 - if s.block != nil && len(s.block.Succs) == 1 { - block = s.block.Succs[0].Index - } - return fmt.Sprintf("Panic %s → b%d", relName(s.X, s), block) + return "panic " + relName(s.X, s) } func (s *Return) String() string { var b bytes.Buffer - b.WriteString("Return") - for _, r := range s.Results { - b.WriteString(" ") + b.WriteString("return") + for i, r := range s.Results { + if i == 0 { + b.WriteString(" ") + } else { + b.WriteString(", ") + } b.WriteString(relName(r, s)) } return b.String() } func (*RunDefers) String() string { - return "RunDefers" + return "rundefers" } func (s *Send) String() string { - return fmt.Sprintf("Send %s %s", relName(s.Chan, s), relName(s.X, s)) + return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s)) } func (recv *Recv) String() string { - from := recv.Parent().pkg() - return fmt.Sprintf("Recv <%s> %s", relType(recv.Type(), from), relName(recv.Chan, recv)) + return fmt.Sprintf("<-%s", relName(recv.Chan, recv)) } func (s *Defer) String() string { - prefix := "Defer " - if s._DeferStack != nil { - prefix += "[" + relName(s._DeferStack, s) + "] " + prefix := "defer " + if s.DeferStack != nil { + prefix += "[" + relName(s.DeferStack, s) + "] " } c := printCall(&s.Call, prefix, s) return c @@ -432,26 +460,24 @@ func (s *Select) String() string { } non := "" if !s.Blocking { - non = "Non" + non = "non" } - from := s.Parent().pkg() - return fmt.Sprintf("Select%sBlocking <%s> [%s]", non, relType(s.Type(), from), b.String()) + return fmt.Sprintf("select %sblocking [%s]", non, b.String()) } func (s *Store) String() string { - return fmt.Sprintf("Store {%s} %s %s", - s.Val.Type(), relName(s.Addr, s), relName(s.Val, s)) + return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s)) } func (s *BlankStore) String() string { - return fmt.Sprintf("BlankStore %s", relName(s.Val, s)) + return fmt.Sprintf("_ = %s", relName(s.Val, s)) } func (s *MapUpdate) String() string { - return fmt.Sprintf("MapUpdate %s %s %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s)) + return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s)) } -func (s *DebugRef) String() string { +func (s *debugRef) String() string { p := s.Parent().Prog.Fset.Position(s.Pos()) var descr any if s.object != nil { @@ -513,27 +539,16 @@ 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(deref(mem.Type()), from)) } } fmt.Fprintf(buf, "\n") } -func (v *MultiConvert) String() string { - from := v.Parent().Pkg.Pkg - - var b strings.Builder - b.WriteString(printConv("MultiConvert", v, v.X)) - b.WriteString(" [") - for i, s := range v.from.Terms { - for j, d := range v.to.Terms { - if i != 0 || j != 0 { - b.WriteString(" | ") - } - fmt.Fprintf(&b, "%s -> %s", relTerm(s, from), relTerm(d, from)) - } +func commaOk(x bool) string { + if x { + return ",ok" } - b.WriteString("]") - return b.String() + return "" } diff --git a/vendor/honnef.co/go/tools/go/ir/sanity.go b/vendor/honnef.co/go/tools/go/ir/sanity.go index 5663b4cf..ab379ed8 100644 --- a/vendor/honnef.co/go/tools/go/ir/sanity.go +++ b/vendor/honnef.co/go/tools/go/ir/sanity.go @@ -94,13 +94,6 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { switch instr := instr.(type) { case *If, *Jump, *Return, *Panic, *Unreachable, *ConstantSwitch: s.errorf("control flow instruction not at end of block") - case *Sigma: - if idx > 0 { - prev := s.block.Instrs[idx-1] - if _, ok := prev.(*Sigma); !ok { - s.errorf("Sigma instruction follows a non-Sigma: %T", prev) - } - } case *Phi: if idx == 0 { // It suffices to apply this check to just the first phi node. @@ -110,18 +103,20 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { } else { prev := s.block.Instrs[idx-1] switch prev.(type) { - case *Phi, *Sigma: + case *Phi: default: - s.errorf("Phi instruction follows a non-Phi, non-Sigma: %T", prev) + s.errorf("Phi instruction follows a non-Phi: %T", prev) } } if ne, np := len(instr.Edges), len(s.block.Preds); ne != np { s.errorf("phi node has %d edges but %d predecessors", ne, np) - } else { for i, e := range instr.Edges { if e == nil { s.errorf("phi node '%s' has no value for edge #%d from %s", instr.Comment(), i, s.block.Preds[i]) + } else if !types.Identical(instr.typ, e.Type()) { + s.errorf("phi node '%s' has a different type (%s) for edge #%d from %s (%s)", + instr.Comment(), instr.Type(), i, s.block.Preds[i], e.Type()) } } } @@ -133,19 +128,21 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { case *BinOp: case *Call: + if common := instr.Call; common.IsInvoke() { + if !types.IsInterface(common.Value.Type()) { + s.errorf("invoke on %s (%s) which is not an interface type (or type param)", common.Value, common.Value.Type()) + } + } case *ChangeInterface: case *ChangeType: case *SliceToArrayPointer: case *SliceToArray: case *Convert: - tsetInstrX := typeutil.NewTypeSet(instr.X.Type().Underlying()) - tsetInstr := typeutil.NewTypeSet(instr.Type().Underlying()) - ok1 := tsetInstr.Any(func(term *types.Term) bool { _, ok := term.Type().Underlying().(*types.Basic); return ok }) - ok2 := tsetInstrX.Any(func(term *types.Term) bool { _, ok := term.Type().Underlying().(*types.Basic); return ok }) - if !ok1 && !ok2 { - s.errorf("convert %s -> %s: at least one type set must contain basic type", instr.X.Type(), instr.Type()) + if from := instr.X.Type(); !isBasicConvTypes(from) { + if to := instr.Type(); !isBasicConvTypes(to) { + s.errorf("convert %s -> %s: at least one type must be basic (or all basic, []byte, or []rune)", from, to) + } } - case *MultiConvert: case *Defer: case *Extract: @@ -163,7 +160,13 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { if numFree != numBind { s.errorf("MakeClosure has %d Bindings for function %s with %d free vars", numBind, instr.Fn, numFree) - + } else { + for i, fv := range instr.Fn.(*Function).FreeVars { + if !types.Identical(instr.Bindings[i].Type(), fv.Type()) { + s.errorf("MakeClosure binding %d for %s has type %s, expected %s", + i, fv.Name(), instr.Bindings[i].Type(), fv.Type()) + } + } } if recv := instr.Type().(*types.Signature).Recv(); recv != nil { s.errorf("MakeClosure's type includes receiver %s", recv.Type()) @@ -174,22 +177,44 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { case *MakeSlice: case *MapUpdate: case *Next: + rng, ok := instr.Iter.(*Range) + if !ok { + s.errorf("Next: Iter is %T, not *Range", instr.Iter) + } + var ek, ev types.Type + switch xt := typeutil.CoreType(rng.X.Type()).(type) { + case *types.Basic: + if types.Default(xt) != tString { + s.errorf("Next: basic operand of Next.Iter (Range) is %s, want string or untyped string", xt) + } + ek, ev = tInt, tRune + case *types.Map: + ek, ev = xt.Key(), xt.Elem() + } + + res := instr.Type().(*types.Tuple) // (ok bool, k K, v V), but K or V may be invalid if unused + if !types.Identical(res.At(1).Type(), ek) && res.At(1).Type() != tInvalid { + s.errorf("Next: key type %s does not match map key type %s", res.At(1).Type(), ek) + } + if !types.Identical(res.At(2).Type(), ev) && res.At(2).Type() != tInvalid { + s.errorf("Next: value type %s does not match map value type %s", res.At(2).Type(), ev) + } + case *Range: case *RunDefers: case *Select: case *Send: case *Slice: case *Store: + if !types.Identical(instr.Val.Type(), typeutil.CoreType(instr.Addr.Type()).(*types.Pointer).Elem()) { + s.errorf("Store: value type %s does not match address type %s", + instr.Val.Type(), instr.Addr.Type()) + } case *TypeAssert: case *UnOp: - case *DebugRef: + case *debugRef: case *BlankStore: case *Load: - case *Parameter: - case *Const: - case *AggregateConst: - case *ArrayConst: - case *GenericConst: case *Recv: case *TypeSwitch: case *CompositeValue: @@ -209,6 +234,8 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { t := v.Type() if t == nil { s.errorf("no type: %s = %s", v.Name(), v) + } else if t == tDeferStack { + // not a proper type; ignore. } else if b, ok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 { if _, ok := v.(*Const); !ok { s.errorf("instruction has 'untyped' result: %s = %s : %s", v.Name(), v, t) @@ -255,18 +282,23 @@ func (s *sanity) checkFinalInstr(instr Instruction) { } case *Panic: - if nsuccs := len(s.block.Succs); nsuccs != 1 { - s.errorf("Panic-terminated block has %d successors; expected one", nsuccs) + if nsuccs := len(s.block.Succs); nsuccs != 0 { + s.errorf("Panic-terminated block has %d successors; expected none", nsuccs) return } case *Unreachable: - if nsuccs := len(s.block.Succs); nsuccs != 1 { - s.errorf("Unreachable-terminated block has %d successors; expected one", nsuccs) + if nsuccs := len(s.block.Succs); nsuccs != 0 { + s.errorf("Unreachable-terminated block has %d successors; expected none", nsuccs) return } case *ConstantSwitch: + if nsuccs := len(s.block.Succs); nsuccs != len(instr.Conds) { + s.errorf("ConstantSwitch-terminated block has %d successors for %d constants", + nsuccs, len(instr.Conds)) + return + } default: s.errorf("non-control flow instruction at end of block") @@ -284,8 +316,9 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) { } // Check all blocks are reachable. - // (The entry block is always implicitly reachable, the exit block may be unreachable.) - if index > 1 && len(b.Preds) == 0 { + // (The entry block is always implicitly reachable, + // as is the Recover block, if any.) + if (index > 0 && b != b.parent.Recover) && len(b.Preds) == 0 { s.warnf("unreachable block") if b.Instrs == nil { // Since this block is about to be pruned, @@ -352,7 +385,7 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) { // Check that "untyped" types only appear on constant operands. if _, ok := (*op).(*Const); !ok { - if basic, ok := types.Unalias((*op).Type()).(*types.Basic); ok { + if basic, ok := (*op).Type().Underlying().(*types.Basic); ok { if basic.Info()&types.IsUntyped != 0 { s.errorf("operand #%d of %s is untyped: %s", i, instr, basic) } @@ -413,40 +446,139 @@ func (s *sanity) checkReferrerList(v Value) { } } +func (s *sanity) checkFunctionParams() { + signature := s.fn.Signature + params := s.fn.Params + + // startSigParams is the start of signature.Params() within params. + startSigParams := 0 + if signature.Recv() != nil { + startSigParams = 1 + } + + if startSigParams+signature.Params().Len() != len(params) { + s.errorf("function has %d parameters in signature but has %d after building", + startSigParams+signature.Params().Len(), len(params)) + return + } + + for i, param := range params { + var sigType types.Type + si := i - startSigParams + if si < 0 { + sigType = signature.Recv().Type() + } else { + sigType = signature.Params().At(si).Type() + } + + if !types.Identical(sigType, param.Type()) { + s.errorf("expect type %s in signature but got type %s in param %d", sigType, param.Type(), i) + } + } +} + +// checkTransientFields checks whether all transient fields of Function are cleared. +func (s *sanity) checkTransientFields() { + fn := s.fn + if fn.build != nil { + s.errorf("function transient field 'build' is not nil") + } + if fn.currentBlock != nil { + s.errorf("function transient field 'currentBlock' is not nil") + } + if fn.vars != nil { + s.errorf("function transient field 'vars' is not nil") + } + if fn.results != nil { + s.errorf("function transient field 'results' is not nil") + } + if fn.returnVars != nil { + s.errorf("function transient field 'returnVars' is not nil") + } + if fn.targets != nil { + s.errorf("function transient field 'targets' is not nil") + } + if fn.lblocks != nil { + s.errorf("function transient field 'lblocks' is not nil") + } + if fn.subst != nil { + s.errorf("function transient field 'subst' is not nil") + } + if fn.jump != nil { + s.errorf("function transient field 'jump' is not nil") + } + if fn.deferstack != nil { + s.errorf("function transient field 'deferstack' is not nil") + } + if fn.source != nil { + s.errorf("function transient field 'source' is not nil") + } + if fn.exits != nil { + s.errorf("function transient field 'exits' is not nil") + } + if fn.uniq != 0 { + s.errorf("function transient field 'uniq' is not zero") + } +} + func (s *sanity) checkFunction(fn *Function) bool { - // TODO(adonovan): check Function invariants: - // - check params match signature - // - check transient fields are nil - // - warn if any fn.Locals do not appear among block instructions. s.fn = fn + s.checkFunctionParams() + s.checkTransientFields() + + // TODO(taking): Sanity check origin, typeparams, and typeargs. if fn.Prog == nil { s.errorf("nil Prog") } var buf bytes.Buffer - _ = fn.String() // must not crash - _ = fn.RelString(fn.pkg()) // must not crash - WriteFunction(&buf, fn) // must not crash + _ = fn.String() // must not crash + _ = fn.RelString(fn.relPkg()) // must not crash + WriteFunction(&buf, fn) // must not crash // All functions have a package, except delegates (which are // shared across packages, or duplicated as weak symbols in a // separate-compilation model), and error.Error. if fn.Pkg == nil { - switch fn.Synthetic { - case SyntheticWrapper, SyntheticBound, SyntheticThunk, SyntheticGeneric: - default: - if !strings.HasSuffix(fn.name, "Error") { - s.errorf("nil Pkg") - } + if strings.HasPrefix(fn.Synthetic, "from type information (on demand)") || + strings.HasPrefix(fn.Synthetic, "wrapper ") || + strings.HasPrefix(fn.Synthetic, "bound ") || + strings.HasPrefix(fn.Synthetic, "thunk ") || + strings.HasSuffix(fn.name, "Error") || + strings.HasPrefix(fn.Synthetic, "instance ") || + strings.HasPrefix(fn.Synthetic, "instantiation ") || + fn.parent != nil && fn.parent.hasTypeArgs() /* anon fun in instance */ { + // ok + } else { + s.errorf("nil Pkg") } } - if syn, src := fn.Synthetic == 0, fn.source != nil; src != syn { - if _, ok := fn.source.(*ast.RangeStmt); !ok || fn.Synthetic != SyntheticRangeOverFuncYield { - // Only range-over-func yield functions are synthetic and have syntax + if src, syn := fn.Synthetic == "", fn.Syntax() != nil; src != syn { + if fn.hasTypeArgs() && fn.Prog.mode&InstantiateGenerics != 0 { + // ok (instantiation with InstantiateGenerics on) + } else if fn.hasTypeArgs() && fn.topLevelOrigin != nil { + // ok (we always have the syntax set for instantiation) + } else if _, rng := fn.syntax.(*ast.RangeStmt); rng && fn.Synthetic == "range-over-func yield" { + // ok (range-func-yields are both synthetic and keep syntax) + } else { s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn) } } + + // Build the set of valid referrers. + s.instrs = make(map[Instruction]struct{}) + + // instrs are the instructions that are present in the function. + for instr := range fn.instrs() { + s.instrs[instr] = unit{} + } + + // Check all Locals allocations appear in the function instruction. for i, l := range fn.Locals { + if _, present := s.instrs[l]; !present { + s.warnf("function doesn't contain Local alloc %s", l.Name()) + } + if l.Parent() != fn { s.errorf("Local %s at index %d has wrong parent", l.Name(), i) } @@ -454,13 +586,6 @@ func (s *sanity) checkFunction(fn *Function) bool { s.errorf("Local %s at index %d has Heap flag set", l.Name(), i) } } - // Build the set of valid referrers. - s.instrs = make(map[Instruction]struct{}) - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - s.instrs[instr] = struct{}{} - } - } for i, p := range fn.Params { if p.Parent() != fn { s.errorf("Param %s at index %d has wrong parent", p.Name(), i) @@ -473,7 +598,6 @@ func (s *sanity) checkFunction(fn *Function) bool { } if !types.Identical(p.Type(), sig.Params().At(j).Type()) { s.errorf("Param %s at index %d has wrong type (%s, versus %s in Signature)", p.Name(), i, p.Type(), sig.Params().At(j).Type()) - } } @@ -498,12 +622,18 @@ func (s *sanity) checkFunction(fn *Function) bool { } s.checkBlock(b, i) } + if fn.Recover != nil && fn.Blocks[fn.Recover.Index] != fn.Recover { + s.errorf("Recover block is not in Blocks slice") + } s.block = nil for i, anon := range fn.AnonFuncs { if anon.Parent() != fn { s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s", i, anon, anon, anon.Parent()) } + if i != int(anon.anonIdx) { + s.errorf("AnonFuncs[%d]=%s but %s.anonIdx=%d", i, anon, anon, anon.anonIdx) + } } s.fn = nil return !s.insane @@ -516,6 +646,19 @@ func sanityCheckPackage(pkg *Package) { if pkg.Pkg == nil { panic(fmt.Sprintf("Package %s has no Object", pkg)) } + if pkg.info != nil { + panic(fmt.Sprintf("package %s field 'info' is not cleared", pkg)) + } + if pkg.files != nil { + panic(fmt.Sprintf("package %s field 'files' is not cleared", pkg)) + } + if pkg.created != nil { + panic(fmt.Sprintf("package %s field 'created' is not cleared", pkg)) + } + if pkg.initVersion != nil { + panic(fmt.Sprintf("package %s field 'initVersion' is not cleared", pkg)) + } + _ = pkg.String() // must not crash for name, mem := range pkg.Members { diff --git a/vendor/honnef.co/go/tools/go/ir/scc.go b/vendor/honnef.co/go/tools/go/ir/scc.go new file mode 100644 index 00000000..d7f74085 --- /dev/null +++ b/vendor/honnef.co/go/tools/go/ir/scc.go @@ -0,0 +1,106 @@ +package ir + +import ( + "math" +) + +type sccState struct { + fn *Function + index int + lows BlockMap[int] + F []int + sccs []*SCC +} + +func (s *sccState) scc(v *BasicBlock) { + // This is Tarjan's algorithm, with some optimizations from [1] applied. + // + // [1] R. E. Tarjan and U. Zwick, “Finding strong components using depth-first search,” Apr. 11, 2022, arXiv: arXiv:2201.07197. doi: 10.48550/arXiv.2201.07197. + vi := v.Index + lows := s.lows + + s.index += 2 + lows[vi] = s.index + + for _, w := range v.Succs { + wi := w.Index + + if lows[wi] == 0 { + s.scc(w) + } + + if lows[wi] < lows[vi] { + lows[vi] = lows[wi] | 1 + } + } + + if lows[vi]&1 == 0 { + leaderLow := lows[vi] + + scc := &SCC{ + Index: len(s.sccs), + } + F := s.F + for lows[F[len(F)-1]] >= leaderLow { + x := F[len(F)-1] + F = F[:len(F)-1] + + lows[x] = math.MaxInt + scc.Blocks = append(scc.Blocks, s.fn.Blocks[x]) + s.fn.Blocks[x].SCC = scc + } + s.F = F + + lows[vi] = math.MaxInt + scc.Blocks = append(scc.Blocks, v) + v.SCC = scc + s.sccs = append(s.sccs, scc) + } else { + s.F = append(s.F, vi) + } +} + +// buildSCCs computes the strongly connected components of fn's control flow graph. +func buildSCCs(fn *Function) { + n := len(fn.Blocks) + + if n == 0 { + return + } + + state := sccState{ + fn: fn, + index: 0, + // The +1 makes space for the sentinel value stored in F. + lows: make(BlockMap[int], n+1), + F: []int{n}, + } + + // All blocks are reachable from the entry block. + state.scc(fn.Blocks[0]) + + seen := make([]bool, len(state.sccs)) + for _, scc := range state.sccs { + clear(seen) + for _, b := range scc.Blocks { + for _, succ := range b.Succs { + if succ.SCC != b.SCC && !seen[succ.SCC.Index] { + b.SCC.Succs = append(b.SCC.Succs, succ.SCC) + succ.SCC.Preds = append(succ.SCC.Preds, b.SCC) + seen[succ.SCC.Index] = true + } + } + } + } + + fn.SCCs = state.sccs + + // This iterates over the SCCs in reverse topological order, i.e., "bottom + // up". This allows us to build up reachability sets. + for _, scc := range fn.SCCs { + scc.reachable.SetBit(&scc.reachable, scc.Index, 1) + for _, succ := range scc.Succs { + scc.reachable.Or(&scc.reachable, &succ.reachable) + } + } +} diff --git a/vendor/honnef.co/go/tools/go/ir/source.go b/vendor/honnef.co/go/tools/go/ir/source.go index 5e3ac28b..bd05db73 100644 --- a/vendor/honnef.co/go/tools/go/ir/source.go +++ b/vendor/honnef.co/go/tools/go/ir/source.go @@ -80,14 +80,23 @@ func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function } case *ast.FuncDecl: - // Declared function/method. - fn := findNamedFunc(pkg, decl.Pos()) - if fn == nil && decl.Recv == nil && decl.Name.Name == "init" { + if decl.Recv == nil && decl.Name.Name == "init" { + // Explicit init() function. + for _, b := range pkg.init.Blocks { + for _, instr := range b.Instrs { + if instr, ok := instr.(*Call); ok { + if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos { + return callee + } + } + } + } // Hack: return non-nil when IR is not yet // built so that HasEnclosingFunction works. return pkg.init } - return fn + // Declared function/method. + return findNamedFunc(pkg, decl.Name.NamePos) } } return nil // not in any function @@ -96,9 +105,25 @@ func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function // findNamedFunc returns the named function whose FuncDecl.Ident is at // position pos. func findNamedFunc(pkg *Package, pos token.Pos) *Function { - for _, fn := range pkg.Functions { - if fn.Pos() == pos { - return fn + // Look at all package members and method sets of named types. + // Not very efficient. + for _, mem := range pkg.Members { + switch mem := mem.(type) { + case *Function: + if mem.Pos() == pos { + return mem + } + case *Type: + mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type())) + for i, n := 0, mset.Len(); i < n; i++ { + // Don't call Program.Method: avoid creating wrappers. + obj := mset.At(i).Obj().(*types.Func) + if obj.Pos() == pos { + // obj from MethodSet may not be the origin type. + m := obj.Origin() + return pkg.values[m].(*Function) + } + } } } return nil @@ -127,19 +152,9 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function { // EnclosingFunction to locate the Function, then ValueForExpr to find // the ir.Value.) func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) { - if f.debugInfo() { // (opt) - e = unparen(e) - for _, b := range f.Blocks { - for _, instr := range b.Instrs { - if ref, ok := instr.(*DebugRef); ok { - if ref.Expr == e { - return ref.X, ref.IsAddr - } - } - } - } - } - return + e = ast.Unparen(e) + entry := f.exprToValue[e] + return entry.v, entry.isAddr } // --- Lookup functions for source-level named entities (types.Objects) --- @@ -151,25 +166,26 @@ func (prog *Program) Package(obj *types.Package) *Package { return prog.packages[obj] } -// packageLevelValue returns the package-level value corresponding to -// the specified named object, which may be a package-level const -// (*Const), var (*Global) or func (*Function) of some package in -// prog. It returns nil if the object is not found. -func (prog *Program) packageLevelValue(obj types.Object) Value { +// packageLevelMember returns the package-level member corresponding +// to the specified symbol, which may be a package-level const +// (*NamedConst), var (*Global) or func/method (*Function) of some +// package in prog. +// +// It returns nil if the object belongs to a package that has not been +// created by prog.CreatePackage. +func (prog *Program) packageLevelMember(obj types.Object) Member { if pkg, ok := prog.packages[obj.Pkg()]; ok { return pkg.values[obj] } return nil } -// FuncValue returns the concrete Function denoted by the source-level -// named function obj, or nil if obj denotes an interface method. -// -// TODO(adonovan): check the invariant that obj.Type() matches the -// result's Signature, both in the params/results and in the receiver. +// FuncValue returns the SSA function or (non-interface) method +// denoted by the specified func symbol. It returns nil if the symbol +// denotes an interface method, or belongs to a package that was not +// created by prog.CreatePackage. func (prog *Program) FuncValue(obj *types.Func) *Function { - obj = obj.Origin() - fn, _ := prog.packageLevelValue(obj).(*Function) + fn, _ := prog.packageLevelMember(obj).(*Function) return fn } @@ -184,8 +200,8 @@ func (prog *Program) ConstValue(obj *types.Const) *Const { return NewConst(obj.Val(), obj.Type(), nil) } // Package-level named constant? - if v := prog.packageLevelValue(obj); v != nil { - return v.(*Const) + if v := prog.packageLevelMember(obj); v != nil { + return v.(*NamedConst).Value } return NewConst(obj.Val(), obj.Type(), nil) } @@ -244,18 +260,12 @@ func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (val } // Other ident? - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - if dr, ok := instr.(*DebugRef); ok { - if dr.Pos() == id.Pos() { - return dr.X, dr.IsAddr - } - } - } + if v, ok := fn.exprToValue[id]; ok { + return v.v, v.isAddr } // Defining ident of package-level var? - if v := prog.packageLevelValue(obj); v != nil { + if v := prog.packageLevelMember(obj); v != nil { return v.(*Global), true } diff --git a/vendor/honnef.co/go/tools/go/ir/ssa.go b/vendor/honnef.co/go/tools/go/ir/ssa.go index cd8ae012..e9277360 100644 --- a/vendor/honnef.co/go/tools/go/ir/ssa.go +++ b/vendor/honnef.co/go/tools/go/ir/ssa.go @@ -5,7 +5,7 @@ package ir // This package defines a high-level intermediate representation for -// Go programs using static single-information (SSI) form. +// Go programs using static single-assignment (SSA) form. import ( "fmt" @@ -13,10 +13,16 @@ import ( "go/constant" "go/token" "go/types" + "iter" "math/big" + "reflect" + "slices" + "strings" "sync" + "unsafe" "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/xtools-internal/typeparams" ) const ( @@ -30,18 +36,31 @@ type ID int // A Program is a partial or complete Go program converted to IR form. type Program struct { Fset *token.FileSet // position information for the files of this Program - PrintFunc string // create ir.html for function specified in PrintFunc imported map[string]*Package // all importable Packages, keyed by import path - packages map[*types.Package]*Package // all loaded Packages, keyed by object + packages map[*types.Package]*Package // all created Packages mode BuilderMode // set of mode bits for IR construction MethodSets typeutil.MethodSetCache // cache of type-checker's method-sets - methodsMu sync.Mutex // guards the following maps: - methodSets typeutil.Map[*methodSet] // maps type to its concrete methodSet - runtimeTypes typeutil.Map[bool] // types for which rtypes are needed - canon typeutil.Map[types.Type] // type canonicalization map + canon *canonizer // type canonicalization map + ctxt *types.Context // cache for type checking instantiations - noReturn func(*types.Func) bool + methodsMu sync.Mutex + methodSets typeutil.Map[*methodSet] // maps type to its concrete *methodSet + + // memoization of whether a type refers to type parameters + hasParamsMu sync.Mutex + hasParams typeparams.Free + + // set of concrete types used as MakeInterface operands + makeInterfaceTypesMu sync.Mutex + makeInterfaceTypes map[types.Type]unit // (may contain redundant identical types) + + // objectMethods is a memoization of objectMethod + // to avoid creation of duplicate methods from type information. + objectMethodsMu sync.Mutex + objectMethods map[*types.Func]*Function + + noReturn func(*types.Func) bool // (optional) predicate that decides whether a given call cannot return } // A Package is a single analyzed Go package containing Members for @@ -53,14 +72,14 @@ type Program struct { // initializer) and "init#%d", the nth declared init function, // and unspecified other things too. type Package struct { - Prog *Program // the owning program - Pkg *types.Package // the corresponding go/types.Package - Members map[string]Member // all package members keyed by name (incl. init and init#%d) - Functions []*Function // all functions, excluding anonymous ones - values map[types.Object]Value // package members (incl. types and methods), keyed by object - init *Function // Func("init"); the package's init function - debug bool // include full debug info in this package - printFunc string // which function to print in HTML form + Prog *Program // the owning program + Pkg *types.Package // the corresponding go/types.Package + Members map[string]Member // all package members keyed by name (incl. init and init#%d) + Functions []*Function // all functions, excluding anonymous ones + values map[types.Object]Member // mapping of package objects to members (incl. methods). Contains *NamedConst, *Global, *Function (values but not types) + init *Function // Func("init"); the package's init function + debug bool // include full debug info in this package + syntax bool // package was loaded from syntax // The following fields are set transiently, then cleared // after building. @@ -68,6 +87,7 @@ type Package struct { ninit int32 // number of init functions info *types.Info // package type information files []*ast.File // package ASTs + created []*Function // members created as a result of building this package (includes declared functions, wrappers) initVersion map[ast.Expr]string // goversion to use for each global var init expr } @@ -106,8 +126,6 @@ type NamedConst struct { // A Value is an IR value that can be referenced by an instruction. type Value interface { - setID(ID) - // Name returns the name of this value, and determines how // this Value appears when used as an operand of an // Instruction. @@ -122,17 +140,6 @@ type Value interface { // and may not even be unique within a function. Name() string - // ID returns the ID of this value. IDs are unique within a single - // function and are densely numbered, but may contain gaps. - // Values and other Instructions share the same ID space. - // Globally, values are identified by their addresses. However, - // IDs exist to facilitate efficient storage of mappings between - // values and data when analysing functions. - // - // NB: IDs are allocated late in the IR construction process and - // are not available to early stages of said process. - ID() ID - // If this value is an Instruction, String returns its // disassembled form; otherwise it returns unspecified // human-readable information about the Value, such as its @@ -193,15 +200,15 @@ type Instruction interface { // String returns the disassembled form of this value. // // Examples of Instructions that are Values: - // "BinOp {+} t1 t2" (BinOp) - // "Call len t1" (Call) + // "x + y" (BinOp) + // "len([])" (Call) // Note that the name of the Value is not printed. // // Examples of Instructions that are not Values: - // "Return t1" (Return) - // "Store {int} t2 t1" (Store) + // "return x" (Return) + // "*y = x" (Store) // - // (The separation of Value.Name() from Value.String() is useful + // (The separation Value.Name() from Value.String() is useful // for some analyses which distinguish the operation from the // value it defines, e.g., 'y = local int' is both an allocation // of memory 'local int' and a definition of a pointer y.) @@ -270,10 +277,7 @@ type Instruction interface { // use the more specific and informative Value or Instruction // interfaces where appropriate. type Node interface { - setID(ID) - // Common methods: - ID() ID String() string Source() ast.Node Pos() token.Pos @@ -284,39 +288,6 @@ type Node interface { Referrers() *[]Instruction // nil for non-Values } -type Synthetic int - -const ( - SyntheticLoadedFromExportData Synthetic = iota + 1 - SyntheticPackageInitializer - SyntheticThunk - SyntheticWrapper - SyntheticBound - SyntheticGeneric - SyntheticRangeOverFuncYield -) - -func (syn Synthetic) String() string { - switch syn { - case SyntheticLoadedFromExportData: - return "loaded from export data" - case SyntheticPackageInitializer: - return "package initializer" - case SyntheticThunk: - return "thunk" - case SyntheticWrapper: - return "wrapper" - case SyntheticBound: - return "bound" - case SyntheticGeneric: - return "generic" - case SyntheticRangeOverFuncYield: - return "range-over-func yield" - default: - return fmt.Sprintf("Synthetic(%d)", syn) - } -} - // Function represents the parameters, results, and code of a function // or method. // @@ -331,6 +302,11 @@ func (syn Synthetic) String() string { // the disassembly. // To iterate over the blocks in dominance order, use DomPreorder(). // +// Recover is an optional second entry point to which control resumes +// after a recovered panic. The Recover block may contain only a return +// statement, preceded by a load of the function's named return +// parameters, if any. +// // A nested function (Parent()!=nil) that refers to one or more // lexically enclosing local variables ("free variables") has FreeVars. // Such functions cannot be called directly but require a @@ -346,7 +322,7 @@ func (syn Synthetic) String() string { // // Pos() returns the declaring ast.FuncLit.Type.Func or the position // of the ast.FuncDecl.Name, if the function was explicit in the -// source. Synthetic wrappers, for which Synthetic != "", may share +// source. Synthetic wrappers, for which Synthetic != "", may share // the same position as the function they wrap. // Syntax.Pos() always returns the position of the declaring "func" token. // @@ -354,6 +330,7 @@ func (syn Synthetic) String() string { // the loop body is transformed into a synthetic anonymous function // that is passed as the yield argument in a call to the iterator. // In that case, Function.Source() is the ast.RangeStmt. +// and Function.Syntax is the ast.RangeStmt. // // Synthetic functions, for which Synthetic != "", are functions // that do not appear in the source AST. These include: @@ -361,23 +338,47 @@ func (syn Synthetic) String() string { // - thunks, // - bound functions, // - empty functions built from loaded type information, -// - yield functions created from range-over-func loops, and -// - package init functions. +// - yield functions created from range-over-func loops, +// - package init functions, and +// - instantiations of generic functions. +// +// Synthetic wrapper functions may share the same position +// as the function they wrap. // // Type() returns the function's Signature. +// +// A generic function is a function or method that has uninstantiated type +// parameters (TypeParams() != nil). Consider a hypothetical generic +// method, (*Map[K,V]).Get. It may be instantiated with all +// non-parameterized types as (*Map[string,int]).Get or with +// parameterized types as (*Map[string,U]).Get, where U is a type parameter. +// In both instantiations, Origin() refers to the instantiated generic +// method, (*Map[K,V]).Get, TypeParams() refers to the parameters [K,V] of +// the generic method. TypeArgs() refers to [string,U] or [string,int], +// respectively, and is nil in the generic method. type Function struct { - node - name string - object *types.Func // symbol for declared function (nil for FuncLit or synthetic init) - method *types.Selection // info about provenance of synthetic methods + object *types.Func // symbol for declared function (nil for FuncLit or synthetic init) + method *selection // info about provenance of synthetic methods; thunk => non-nil Signature *types.Signature - generics instanceWrapperMap + pos token.Pos + syntax ast.Node + + // source information + Synthetic string // provenance of synthetic function; "" for true source functions + info *types.Info // type annotations (if syntax != nil) + goversion string // Go version of syntax (NB: init is special) + + parent *Function // enclosing function if anon; nil if global + Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error) + Prog *Program // enclosing program + + exprToValue map[ast.Expr]struct { + v Value + isAddr bool + } - Synthetic Synthetic // provenance of synthetic function; 0 for true source functions - parent *Function // enclosing function if anon; nil if global - Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error) - Prog *Program // enclosing program + buildshared *task // wait for a shared function to be done building (may be nil if <=1 builder ever needs to wait) // These fields are populated only when the function body is built: @@ -385,123 +386,37 @@ type Function struct { FreeVars []*FreeVar // free variables whose values must be supplied by closure Locals []*Alloc // frame-allocated variables of this function Blocks []*BasicBlock // basic blocks of the function; nil => external - Exit *BasicBlock // The function's exit block + Recover *BasicBlock // optional; control transfers here after recovered panic AnonFuncs []*Function // anonymous functions (from FuncLit, RangeStmt) directly beneath this one referrers []Instruction // referring instructions (iff Parent() != nil) - - goversion string // Go version of syntax (NB: init is special) - - // uniq is not stored in functionBody because we need it after function building finishes - uniq int64 // source of unique ints within the source tree while building - - *functionBody -} - -type instanceWrapperMap struct { - h typeutil.Hasher - entries map[uint32][]struct { - key *types.TypeList - val *Function - } - len int -} - -func typeListIdentical(l1, l2 *types.TypeList) bool { - if l1.Len() != l2.Len() { - return false - } - for i := 0; i < l1.Len(); i++ { - t1 := l1.At(i) - t2 := l2.At(i) - if !types.Identical(t1, t2) { - return false - } - } - return true -} - -func (m *instanceWrapperMap) At(key *types.TypeList) *Function { - if m.entries == nil { - m.entries = make(map[uint32][]struct { - key *types.TypeList - val *Function - }) - m.h = typeutil.MakeHasher() - } - - var hash uint32 - for t := range key.Types() { - hash += m.h.Hash(t) - } - - for _, e := range m.entries[hash] { - if typeListIdentical(e.key, key) { - return e.val - } - } - return nil -} - -func (m *instanceWrapperMap) Set(key *types.TypeList, val *Function) { - if m.entries == nil { - m.entries = make(map[uint32][]struct { - key *types.TypeList - val *Function - }) - m.h = typeutil.MakeHasher() - } - - var hash uint32 - for t := range key.Types() { - hash += m.h.Hash(t) - } - for i, e := range m.entries[hash] { - if typeListIdentical(e.key, key) { - m.entries[hash][i].val = val - return - } - } - m.entries[hash] = append(m.entries[hash], struct { - key *types.TypeList - val *Function - }{key, val}) - m.len++ -} - -func (m *instanceWrapperMap) Len() int { - return m.len -} - -type constValue struct { - c Constant - idx int -} - -type functionBody struct { - // The following fields are set transiently during building, - // then cleared. - currentBlock *BasicBlock // where to emit code - vars map[*types.Var]Value // addresses of local variables - results []*Alloc // result allocations of the current function - returnVars []*types.Var // variables for a return statement. Either results or for range-over-func a parent's results - targets *targets // linked stack of branch targets - lblocks map[*types.Label]*lblock // labelled blocks - jump *types.Var // synthetic variable for the yield state (non-nil => range-over-func) - deferstack *types.Var // synthetic variable holding enclosing ssa:deferstack() - sourceFn *Function // nearest enclosing source function - exits []*exit // exits of the function that need to be resolved - - consts map[constKey]constValue - aggregateConsts typeutil.Map[[]*AggregateConst] - - wr *HTMLWriter - fakeExits BlockSet - blocksets [5]BlockSet - hasDefer bool - - // a contiguous block of instructions that will be used by blocks, - // to avoid making multiple allocations. - scratchInstructions []Instruction + anonIdx int32 // position of a nested function in parent's AnonFuncs. fn.Parent()!=nil => fn.Parent().AnonFunc[fn.anonIdx] == fn. + + SCCs []*SCC // SCC DAG in reverse topological order + + recvtypeparams *types.TypeParamList // receiver type parameters of this function. recvtypeparams.Len() > 0 => method on generic or instance of generic type + recvtypeargs []types.Type // type arguments that instantiated recvtypeparams. len(recvtypeargs) > 0 => method on instance of generic type + typeparams *types.TypeParamList // type parameters of this function. typeparams.Len() > 0 => generic or instance of generic function or method + typeargs []types.Type // type arguments that instantiated typeparams. len(typeargs) > 0 => instance of generic function or method + topLevelOrigin *Function // the origin function if this is an instance of a source function. nil if Parent()!=nil. + generic *generic // instances of this function, if generic + + // The following fields are cleared after building. + build buildFunc // algorithm to build function body (nil => built) + currentBlock *BasicBlock // where to emit code + vars map[*types.Var]Value // addresses of local variables + results []*Alloc // result allocations of the current function + returnVars []*types.Var // variables for a return statement. Either results or for range-over-func a parent's results + targets *targets // linked stack of branch targets + lblocks map[*types.Label]*lblock // labelled blocks + subst *subster // type parameter substitutions (if non-nil) + jump *types.Var // synthetic variable for the yield state (non-nil => range-over-func) + deferstack *types.Var // synthetic variable holding enclosing ssa:deferstack() + source *Function // nearest enclosing source function + exits []*exit // exits of the function that need to be resolved + uniq int64 // source of unique ints within the source tree while building + liftableBlockMap BlockMap[liftableBlockDesc] + + blocksets [4]BlockSet } // BasicBlock represents an IR basic block. @@ -529,12 +444,18 @@ type BasicBlock struct { parent *Function // parent function Instrs []Instruction // instructions in order Preds, Succs []*BasicBlock // predecessors and successors + SCC *SCC // strongly connected component succs2 [2]*BasicBlock // initial space for Succs dom domInfo // dominator tree info - pdom domInfo // post-dominator tree info - post int - gaps int // number of nil Instrs (transient) - rundefers int // number of rundefers (transient) + gaps int // number of nil Instrs (transient) + rundefers int // number of rundefers (transient) +} + +type SCC struct { + Index int + Blocks []*BasicBlock + Preds, Succs []*SCC + reachable big.Int } // Pure values ---------------------------------------- @@ -568,42 +489,46 @@ type FreeVar struct { // A Parameter represents an input parameter of a function. type Parameter struct { - register + node - name string - object *types.Var // non-nil + name string + object *types.Var // non-nil + typ types.Type + parent *Function + referrers []Instruction } -// A Const represents the value of a constant expression. -// -// The underlying type of a constant may be any boolean, numeric, or -// string type. In addition, a Const may represent the nil value of -// any reference type---interface, map, channel, pointer, slice, or -// function---but not "untyped nil". -// -// All source-level constant expressions are represented by a Const -// of the same type and value. +// A Const represents a value known at build time. // -// Value holds the exact value of the constant, independent of its -// Type(), using the same representation as package go/constant uses for -// constants, or nil for a typed nil value. +// Consts include true constants of boolean, numeric, and string types, as +// defined by the Go spec; these are represented by a non-nil Value field. // -// Pos() returns token.NoPos. +// Consts also include the "zero" value of any type, of which the nil values +// of various pointer-like types are a special case; these are represented +// by a nil Value field. // // Example printed form: // -// Const {42} -// Const {"test"} -// Const {(3 + 4i)} +// 42:int +// "hello":untyped string +// 3+4i:MyComplex +// nil:*int +// nil:[]string +// [3]int{}:[3]int +// struct{x string}{}:struct{x string} +// 0:interface{int|int64} +// nil:interface{bool|int} // no go/constant representation type Const struct { - register + node + typ types.Type Value constant.Value } type AggregateConst struct { - register + node + typ types.Type Values []Value } @@ -613,33 +538,20 @@ type CompositeValue struct { // Bitmap records which elements were explicitly provided. For example, [4]byte{2: x} would have a bitmap of 0010. Bitmap big.Int // The number of bits set in Bitmap + // TODO(dh): remove NumSet. we can just query the bitmap. NumSet int // Dense list of values in the composite literal. Omitted elements are filled in with zero values. Values []Value } -// TODO add the element's zero constant to ArrayConst -type ArrayConst struct { - register -} - -type GenericConst struct { - register -} - type Constant interface { - Instruction Value aConstant() RelString(*types.Package) string - equal(Constant) bool - setType(types.Type) } func (*Const) aConstant() {} func (*AggregateConst) aConstant() {} -func (*ArrayConst) aConstant() {} -func (*GenericConst) aConstant() {} // A Global is a named Value holding the address of a package-level // variable. @@ -712,60 +624,17 @@ type Builtin struct { // // Example printed form: // -// t1 = StackAlloc <*int> -// t2 = HeapAlloc <*int> (new) +// t0 = local int +// t1 = new int type Alloc struct { register Heap bool index int // dense numbering; for lifting } -var _ Instruction = (*Sigma)(nil) -var _ Value = (*Sigma)(nil) - -// The Sigma instruction represents an SSI σ-node, which splits values -// at branches in the control flow. -// -// Conceptually, σ-nodes exist at the end of blocks that branch and -// constitute parallel assignments to one value per destination block. -// However, such a representation would be awkward to work with, so -// instead we place σ-nodes at the beginning of branch targets. The -// From field denotes to which incoming edge the node applies. -// -// Within a block, all σ-nodes must appear before all non-σ nodes. -// -// Example printed form: -// -// t2 = Sigma [#0] t1 (x) -type Sigma struct { - register - From *BasicBlock - X Value - - live bool // used during lifting -} - -type CopyInfo uint64 - -const ( - CopyInfoUnspecified CopyInfo = 0 - CopyInfoNotNil CopyInfo = 1 << iota - CopyInfoNotZeroLength - CopyInfoNotNegative - CopyInfoSingleConcreteType - CopyInfoClosed -) - -type Copy struct { - register - X Value - Why Instruction - Info CopyInfo -} - // The Phi instruction represents an SSA φ-node, which combines values // that differ across incoming control-flow edges and yields a new -// value. Within a block, all φ-nodes must appear before all non-φ, non-σ +// value. Within a block, all φ-nodes must appear before all non-φ // nodes. // // Pos() returns the position of the && or || for short-circuit @@ -774,7 +643,7 @@ type Copy struct { // // Example printed form: // -// t3 = Phi 2:t1 4:t2 (x) +// t2 = phi [0: t0, 1: t1] type Phi struct { register Edges []Value // Edges[i] is value for Block().Preds[i] @@ -794,9 +663,9 @@ type Phi struct { // // Example printed form: // -// t3 = Call <()> println t1 t2 -// t4 = Call <()> foo$1 -// t6 = Invoke t5.String +// t2 = println(t0, t1) +// t4 = t3() +// t7 = invoke t5.Println(...t6) type Call struct { register Call CallCommon @@ -808,7 +677,7 @@ type Call struct { // // Example printed form: // -// t3 = BinOp {+} t2 t1 +// t1 = t0 + 1:int type BinOp struct { register // One of: @@ -826,7 +695,7 @@ type BinOp struct { // // Example printed form: // -// t2 = UnOp {^} t1 +// t0 = ^x type UnOp struct { register Op token.Token // One of: NOT SUB XOR ! - ^ @@ -856,15 +725,23 @@ type Load struct { // - between (possibly named) pointers to identical base types. // - from a bidirectional channel to a read- or write-channel, // optionally adding/removing a name. +// - between a type (t) and an instance of the type (tσ), i.e. +// Type() == σ(X.Type()) (or X.Type()== σ(Type())) where +// σ is the type substitution of Parent().TypeParams by +// Parent().TypeArgs. // // This operation cannot fail dynamically. // +// Type changes may to be to or from a type parameter (or both). All +// types in the type set of X.Type() have a value-preserving type +// change to all types in the type set of Type(). +// // Pos() returns the ast.CallExpr.Lparen, if the instruction arose // from an explicit conversion in the source. // // Example printed form: // -// t2 = ChangeType <*T> t1 +// t1 = changetype *int <- IntPtr (t0) type ChangeType struct { register X Value @@ -884,6 +761,10 @@ type ChangeType struct { // // A conversion may imply a type name change also. // +// Conversions may to be to or from a type parameter. All types in +// the type set of X.Type() can be converted to all types in the type +// set of Type(). +// // This operation cannot fail dynamically. // // Conversions of untyped string/number/bool constants to a specific @@ -894,7 +775,7 @@ type ChangeType struct { // // Example printed form: // -// t2 = Convert <[]byte> t1 +// t1 = convert []byte <- string (t0) type Convert struct { register X Value @@ -915,9 +796,8 @@ type Convert struct { // t1 = multiconvert D <- S (t0) [*[2]rune <- []rune | string <- []rune] type MultiConvert struct { register - X Value - from typeutil.TypeSet - to typeutil.TypeSet + X Value + from, to types.Type } // ChangeInterface constructs a value of one interface type from a @@ -931,7 +811,7 @@ type MultiConvert struct { // // Example printed form: // -// t2 = ChangeInterface t1 +// t1 = change interface interface{} <- I (t0) type ChangeInterface struct { register X Value @@ -943,9 +823,17 @@ type ChangeInterface struct { // Pos() returns the ast.CallExpr.Lparen, if the instruction arose // from an explicit conversion in the source. // +// Conversion may to be to or from a type parameter. All types in +// the type set of X.Type() must be a slice types that can be converted to +// all types in the type set of Type() which must all be pointer to array +// types. +// +// This operation can fail dynamically if the length of the slice is less +// than the length of the array. +// // Example printed form: // -// t2 = SliceToArrayPointer <*[4]byte> t1 +// t1 = slice to array pointer *[4]byte <- []byte (t0) type SliceToArrayPointer struct { register X Value @@ -980,7 +868,8 @@ type SliceToArray struct { // // Example printed form: // -// t2 = MakeInterface t1 +// t1 = make interface{} <- int (42:int) +// t1 = make Stringer <- t0 type MakeInterface struct { register X Value @@ -996,8 +885,8 @@ type MakeInterface struct { // // Example printed form: // -// t1 = MakeClosure foo$1 t1 t2 -// t5 = MakeClosure (T).foo$bound t4 +// t0 = make closure anon@1.2 [x y z] +// t1 = make closure bound$(main.I).add [i] type MakeClosure struct { register Fn Value // always a *Function @@ -1014,8 +903,8 @@ type MakeClosure struct { // // Example printed form: // -// t1 = MakeMap -// t2 = MakeMap t1 +// t1 = make map[string]int t0 +// t1 = make StringIntMap t0 type MakeMap struct { register Reserve Value // initial space reservation; nil => default @@ -1031,8 +920,8 @@ type MakeMap struct { // // Example printed form: // -// t3 = MakeChan t1 -// t4 = MakeChan t2 +// t1 = make chan int t0 +// t1 = make IntChan t0 type MakeChan struct { register Size Value // int; size of buffer; zero => synchronous. @@ -1053,8 +942,8 @@ type MakeChan struct { // // Example printed form: // -// t3 = MakeSlice <[]string> t1 t2 -// t4 = MakeSlice t1 t2 +// t2 = make []string t1 t0 +// t2 = make StringSlice t1 t0 type MakeSlice struct { register Len Value @@ -1076,7 +965,7 @@ type MakeSlice struct { // // Example printed form: // -// t4 = Slice <[]int> t3 t2 t1 +// t2 = slice t0[t1:] type Slice struct { register X Value // slice, string, or *array @@ -1094,15 +983,18 @@ type Slice struct { // Type() returns a (possibly named) *types.Pointer. // // Pos() returns the position of the ast.SelectorExpr.Sel for the -// field, if explicit in the source. +// field, if explicit in the source. For implicit selections, returns +// the position of the inducing explicit selection. If produced for a +// struct literal S{f: e}, it returns the position of the colon; for +// S{e} it returns the start of expression e. // // Example printed form: // -// t2 = FieldAddr <*int> [0] (X) t1 +// t1 = &t0.name [#1] type FieldAddr struct { register X Value // *struct - Field int // field is X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*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. @@ -1112,22 +1004,23 @@ type FieldAddr struct { // package-local identifiers and permit compact representations. // // Pos() returns the position of the ast.SelectorExpr.Sel for the -// field, if explicit in the source. +// field, if explicit in the source. For implicit selections, returns +// the position of the inducing explicit selection. // // Example printed form: // -// t2 = FieldAddr [0] (X) t1 +// t1 = t0.name [#1] type Field struct { register X Value // struct - Field int // index into 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 // index Index of collection X. Index is an integer expression. // -// The elements of maps and strings are not addressable; use StringLookup, MapLookup or -// MapUpdate instead. +// The elements of maps and strings are not addressable; use Lookup (map), +// Index (string), or MapUpdate instead. // // Dynamically, this instruction panics if X evaluates to a nil *array // pointer. @@ -1139,28 +1032,31 @@ type Field struct { // // Example printed form: // -// t3 = IndexAddr <*int> t2 t1 +// t2 = &t0[t1] type IndexAddr struct { register - X Value // slice or *array, + X Value // *array, slice or type parameter with types array, *array, or slice. Index Value // numeric index } -// The Index instruction yields element Index of array X. +// The Index instruction yields element Index of collection X, an array, +// string or type parameter containing an array, a string, a pointer to an, +// array or a slice. // // Pos() returns the ast.IndexExpr.Lbrack for the index operation, if // explicit in the source. // // Example printed form: // -// t3 = Index t2 t1 +// t2 = t0[t1] type Index struct { register - X Value // array + X Value // array, string or type parameter with types array, *array, slice, or string. Index Value // integer index } // The MapLookup instruction yields element Index of collection X, a map. +// Index is the appropriate key type. // // If CommaOk, the result is a 2-tuple of the value above and a // boolean indicating the result of a map membership test for the key. @@ -1170,8 +1066,8 @@ type Index struct { // // Example printed form: // -// t4 = MapLookup t3 t1 -// t6 = MapLookup <(string, bool)> t3 t2 +// t2 = t0[t1] +// t5 = t3[t4],ok type MapLookup struct { register X Value // map @@ -1238,8 +1134,8 @@ type SelectState struct { // // Example printed form: // -// t6 = SelectNonBlocking <(index int, ok bool, int)> [<-t4, t5<-t1] -// t11 = SelectBlocking <(index int, ok bool)> [] +// t3 = select nonblocking [<-t0, t1<-t2] +// t4 = select blocking [] type Select struct { register States []*SelectState @@ -1257,7 +1153,7 @@ type Select struct { // // Example printed form: // -// t2 = Range t1 +// t0 = range "hello":string type Range struct { register X Value // string or map @@ -1280,8 +1176,7 @@ type Range struct { // // Example printed form: // -// t5 = Next <(ok bool, k int, v rune)> t2 -// t5 = Next <(ok bool, k invalid type, v invalid type)> t2 +// t1 = next t0 type Next struct { register Iter Value @@ -1299,6 +1194,9 @@ type Next struct { // is AssertedType's zero value. The components of the pair must be // accessed using the Extract instruction. // +// If Underlying: tests whether interface value X has the underlying +// type AssertedType. +// // If AssertedType is a concrete type, TypeAssert checks whether the // dynamic type in interface X is equal to it, and if so, the result // of the conversion is a copy of the value in the interface. @@ -1313,16 +1211,17 @@ type Next struct { // Type() reflects the actual type of the result, possibly a // 2-types.Tuple; AssertedType is the asserted type. // -// Pos() returns the ast.CallExpr.Lparen if the instruction arose from -// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the -// instruction arose from an explicit e.(T) operation; or the -// ast.CaseClause.Case if the instruction arose from a case of a -// type-switch statement. +// Depending on the TypeAssert's purpose, Pos may return: +// - the ast.CallExpr.Lparen of an explicit T(e) conversion; +// - the ast.TypeAssertExpr.Lparen of an explicit e.(T) operation; +// - the ast.CaseClause.Case of a case of a type-switch statement; +// - the Ident(m).NamePos of an interface method value i.m +// (for which TypeAssert may be used to effect the nil check). // // Example printed form: // -// t2 = TypeAssert t1 -// t4 = TypeAssert <(value fmt.Stringer, ok bool)> t1 +// t1 = typeassert t0.(int) +// t3 = typeassert,ok t2.(T) type TypeAssert struct { register X Value @@ -1338,7 +1237,7 @@ type TypeAssert struct { // // Example printed form: // -// t7 = Extract [1] (ok) t4 +// t1 = extract t0 #1 type Extract struct { register Tuple Value @@ -1356,7 +1255,7 @@ type Extract struct { // // Example printed form: // -// Jump → b1 +// jump done type Jump struct { anInstruction } @@ -1365,15 +1264,12 @@ type Jump struct { // continue after the preceding function call because it terminates // the process. // -// The instruction acts as a control instruction, jumping to the exit -// block. However, this jump will never execute. -// // An Unreachable instruction must be the last instruction of its -// containing BasicBlock. +// containing BasicBlock, which must have no successors. // // Example printed form: // -// Unreachable → b1 +// Unreachable type Unreachable struct { anInstruction } @@ -1389,7 +1285,7 @@ type Unreachable struct { // // Example printed form: // -// If t2 → b1 b2 +// if t0 goto done else body type If struct { anInstruction Cond Value @@ -1429,8 +1325,8 @@ type TypeSwitch struct { // // Example printed form: // -// Return -// Return t1 t2 +// return +// return t0, t1 type Return struct { anInstruction Results []Value @@ -1447,7 +1343,7 @@ type Return struct { // // Example printed form: // -// RunDefers +// rundefers type RunDefers struct { anInstruction } @@ -1455,7 +1351,7 @@ type RunDefers struct { // The Panic instruction initiates a panic with value X. // // A Panic instruction must be the last instruction of its containing -// BasicBlock, which must have one successor, the exit block. +// BasicBlock, which must have no successors. // // NB: 'go panic(x)' and 'defer panic(x)' do not use this instruction; // they are treated as calls to a built-in function. @@ -1465,7 +1361,7 @@ type RunDefers struct { // // Example printed form: // -// Panic t1 +// panic t0 type Panic struct { anInstruction X Value // an interface{} @@ -1480,9 +1376,9 @@ type Panic struct { // // Example printed form: // -// Go println t1 -// Go t3 -// GoInvoke t4.Bar t2 +// go println(t0, t1) +// go t3() +// go invoke t5.Println(...t6) type Go struct { anInstruction Call CallCommon @@ -1491,10 +1387,10 @@ type Go struct { // The Defer instruction pushes the specified call onto a stack of // functions to be called by a RunDefers instruction or by a panic. // -// If _DeferStack != nil, it indicates the defer list that the defer is +// If DeferStack != nil, it indicates the defer list that the defer is // added to. Defer list values come from the Builtin function // ssa:deferstack. Calls to ssa:deferstack() produces the defer stack -// of the current function frame. _DeferStack allows for deferring into an +// of the current function frame. DeferStack allows for deferring into an // alternative function stack than the current function. // // See CallCommon for generic function call documentation. @@ -1503,15 +1399,13 @@ type Go struct { // // Example printed form: // -// Defer println t1 -// Defer t3 -// DeferInvoke t4.Bar t2 +// defer println(t0, t1) +// defer t3() +// defer invoke t5.Println(...t6) type Defer struct { anInstruction - Call CallCommon - _DeferStack Value // stack (from ssa:deferstack() intrinsic) onto which this function is pushed - - // TODO: Exporting _DeferStack and possibly making _DeferStack != nil awaits proposal https://github.com/golang/go/issues/66601. + Call CallCommon + DeferStack Value // stack of deferred functions (from ssa:deferstack() intrinsic) onto which this function is pushed } // The Send instruction sends X on channel Chan. @@ -1520,7 +1414,7 @@ type Defer struct { // // Example printed form: // -// Send t2 t1 +// send t0 <- t1 type Send struct { anInstruction Chan, X Value @@ -1556,7 +1450,7 @@ type Recv struct { // // Example printed form: // -// Store {int} t2 t1 +// *x = y type Store struct { anInstruction Addr Value @@ -1586,7 +1480,7 @@ type BlankStore struct { // // Example printed form: // -// MapUpdate t3 t1 t2 +// t0[t1] = t2 type MapUpdate struct { anInstruction Map Value @@ -1594,17 +1488,22 @@ type MapUpdate struct { Value Value } -// A DebugRef instruction maps a source-level expression Expr to the +// A debugRef instruction maps a source-level expression Expr to the // IR value X that represents the value (!IsAddr) or address (IsAddr) // of that expression. // -// DebugRef is a pseudo-instruction: it has no dynamic effect. +// debugRef is a pseudo-instruction: it has no dynamic effect. // // Pos() returns Expr.Pos(), the start position of the source-level // expression. This is not the same as the "designated" token as // documented at Value.Pos(). e.g. CallExpr.Pos() does not return the // position of the ("designated") Lparen token. // +// If Expr is an *ast.Ident denoting a var or func, Object() returns +// the object; though this information can be obtained from the type +// checker, including it here greatly facilitates debugging. +// For non-Ident expressions, Object() returns nil. +// // DebugRefs are generated only for functions built with debugging // enabled; see Package.SetDebugMode() and the GlobalDebug builder // mode flag. @@ -1622,7 +1521,7 @@ type MapUpdate struct { // ; *ast.CallExpr @ 102:9 is t5 // ; var x float64 @ 109:72 is x // ; address of *ast.CompositeLit @ 216:10 is t0 -type DebugRef struct { +type debugRef struct { anInstruction Expr ast.Expr // the referring expression (never *ast.ParenExpr) object types.Object // the identity of the source var/func @@ -1647,12 +1546,8 @@ type register struct { type node struct { source ast.Node - id ID } -func (n *node) setID(id ID) { n.id = id } -func (n node) ID() ID { return n.id } - func (n *node) setSource(source ast.Node) { n.source = source } func (n *node) Source() ast.Node { return n.source } @@ -1667,10 +1562,19 @@ func (n *node) Pos() token.Pos { // It provides the implementations of the Block and setBlock methods. type anInstruction struct { node + id ID block *BasicBlock // the basic block of this instruction comment string } +func (instr *anInstruction) setID(id ID) { + instr.id = id +} + +func (instr anInstruction) ID() ID { + return instr.id +} + func (instr anInstruction) Comment() string { return instr.comment } @@ -1706,16 +1610,17 @@ func (instr anInstruction) Comment() string { // // Example printed form: // -// t3 = Call <()> println t1 t2 -// Go t3 -// Defer t3 +// t2 = println(t0, t1) +// go t3() +// defer t5(...t6) // // 2. "invoke" mode: when Method is non-nil (IsInvoke), a CallCommon // represents a dynamically dispatched call to an interface method. // In this mode, Value is the interface value and Method is the -// interface's abstract method. Note: an abstract method may be -// shared by multiple interfaces due to embedding; Value.Type() -// provides the specific interface used for this call. +// interface's abstract method. The interface value may be a type +// parameter. Note: an interface method may be shared by multiple +// interfaces due to embedding; Value.Type() provides the specific +// interface used for this call. // // Value is implicitly supplied to the concrete method implementation // as the receiver parameter; in other words, Args[0] holds not the @@ -1723,18 +1628,18 @@ func (instr anInstruction) Comment() string { // // Example printed form: // -// t6 = Invoke t5.String -// GoInvoke t4.Bar t2 -// DeferInvoke t4.Bar t2 +// t1 = invoke t0.String() +// go invoke t3.Run(t2) +// defer invoke t4.Handle(...t5) // // For all calls to variadic functions (Signature().Variadic()), // the last element of Args is a slice. type CallCommon struct { - Value Value // receiver (invoke mode) or func value (call mode) - Method *types.Func // abstract method (invoke mode) - Args []Value // actual parameters (in static method call, includes receiver) - TypeArgs []types.Type - Results Value + Value Value // receiver (invoke mode) or func value (call mode) + Method *types.Func // interface method (invoke mode) + Args []Value // actual parameters (in static method call, includes receiver) + + source ast.Node } // IsInvoke returns true if this call has "invoke" (not "call") mode. @@ -1742,6 +1647,13 @@ func (c *CallCommon) IsInvoke() bool { return c.Method != nil } +func (c *CallCommon) Pos() token.Pos { + if c.source != nil { + return c.source.Pos() + } + return token.NoPos +} + // Signature returns the signature of the called function. // // For an "invoke"-mode call, the signature of the interface method is @@ -1801,6 +1713,10 @@ func (s *Call) Common() *CallCommon { return &s.Call } func (s *Defer) Common() *CallCommon { return &s.Call } func (s *Go) Common() *CallCommon { return &s.Call } +func (s *Call) setSource(source ast.Node) { s.source = source; s.Call.source = source } +func (s *Defer) setSource(source ast.Node) { s.source = source; s.Call.source = source } +func (s *Go) setSource(source ast.Node) { s.source = source; s.Call.source = source } + func (s *Call) Value() *Call { return s } func (s *Defer) Value() *Call { return nil } func (s *Go) Value() *Call { return nil } @@ -1846,7 +1762,187 @@ func (v *Function) Referrers() *[]Instruction { return nil } -func (v *Parameter) Object() types.Object { return v.object } +// Returns returns an iterator over all blocks containing return statements. +func (v *Function) Returns() iter.Seq[*BasicBlock] { + return func(yield func(*BasicBlock) bool) { + for _, b := range v.Blocks { + if _, ok := b.Control().(*Return); ok { + if !yield(b) { + return + } + } + } + } +} + +// Exits returns an iterator over all blocks that have no successors. +func (v *Function) Exits() iter.Seq[*BasicBlock] { + return func(yield func(*BasicBlock) bool) { + for _, b := range v.Blocks { + if len(b.Succs) == 0 { + if !yield(b) { + return + } + } + } + } +} + +// Nodes returns an iterator over the basic block indices. +func (v *Function) Nodes() iter.Seq[int] { + return func(yield func(int) bool) { + for i := range v.Blocks { + if !yield(i) { + return + } + } + } +} + +// NumNodes returns the number of basic blocks in the graph. +func (v *Function) NumNodes() int { return len(v.Blocks) } + +// Out returns an iterator over the successor block indices of a given node. +func (v *Function) Out(node int) iter.Seq[int] { + return func(yield func(int) bool) { + for _, succ := range v.Blocks[node].Succs { + if !yield(succ.Index) { + return + } + } + } +} + +func (v *Parameter) Type() types.Type { return v.typ } +func (v *Parameter) Name() string { return v.name } +func (v *Parameter) Object() types.Object { return v.object } +func (v *Parameter) Referrers() *[]Instruction { return &v.referrers } +func (v *Parameter) Pos() token.Pos { return v.object.Pos() } +func (v *Parameter) Parent() *Function { return v.parent } + +// TypeParams are the function's type parameters if generic or the +// type parameters that were instantiated if fn is an instantiation. +// +// Specifically, the resulting list behaves like: +// +// func f // [] +// func f[P] // [P] +// func (T) m // [] +// func (T) m[P] // [P] +// func (T[P]) m // [P] +// func (T[P]) m[Q] // [P (index=0), Q (index=0)] +// +// Note that receiver type parameters precede other type parameters. +// Also, type parameters may have the same index if they come from +// different source type parameter lists. +func (fn *Function) TypeParams() *types.TypeParamList { + return consTypeParamLists(fn.recvtypeparams, fn.typeparams) +} + +func consTypeParamLists(l, r *types.TypeParamList) *types.TypeParamList { + if l.Len() == 0 { + return r + } + if r.Len() == 0 { + return l + } + + tpars := make([]*types.TypeParam, l.Len()+r.Len()) + for i := range l.Len() { + tpars[i] = l.At(i) + } + for i := range r.Len() { + tpars[i+l.Len()] = r.At(i) + } + // This logic unsafely assumes (and asserts) that the layout of the + // TypeParamList is identical to that of a slice of TypeParams. This + // is a hack while we work on getting a constructor for TypeParamList + // approved (see go.dev/issue/79603). + t := reflect.TypeFor[types.TypeParamList]() + if t.NumField() != 1 { + panic("TypeParamList has unexpected fields") + } + if f := t.Field(0); f.Offset != 0 || f.Type != reflect.TypeFor[[]*types.TypeParam]() { + panic("TypeParamList field is not []*TypeParam") + } + return (*types.TypeParamList)(unsafe.Pointer(&tpars)) +} + +// TypeArgs are the types that TypeParams() were instantiated by to create fn +// from fn.Origin(). +// +// Specifically, the resulting slice behaves like: +// +// f // [] +// f[int] // [int] +// T.m // [] +// T.m[int] // [int] +// T[int].m // [int] +// T[int].m[uint] // [int, uint] +// +// Note that receiver type arguments precede other type arguments. +func (fn *Function) TypeArgs() []types.Type { + return slices.Concat(fn.recvtypeargs, fn.typeargs) +} + +// Origin returns the generic function from which fn was instantiated, +// or nil if fn is not an instantiation. +func (fn *Function) Origin() *Function { + if fn.parent != nil && fn.parent.hasTypeArgs() { + // Nested functions are BUILT at a different time than their instances. + // Build declared package if not yet BUILT. This is not an expected use + // case, but is simple and robust. + fn.declaredPackage().Build() + } + return origin(fn) +} + +// hasTypeParams returns whether fn has any type parameters +func (fn *Function) hasTypeParams() bool { + return fn.recvtypeparams.Len()+fn.typeparams.Len() > 0 +} + +// hasTypeArgs returns whether fn has any type arguments +func (fn *Function) hasTypeArgs() bool { + return len(fn.recvtypeargs)+len(fn.typeargs) > 0 +} + +// subrtargs returns fn's receiver type parameters substituted with receiver type arguments +func (fn *Function) subrtargs(m *types.Func) []types.Type { + return fn.subst.types(receiverTypeArgs(m)) +} + +// subtargs returns fn's type parameters substituted with (possibly implied) type arguments +func (fn *Function) subtargs(id *ast.Ident) []types.Type { + return fn.subst.types(instanceArgs(fn.info, id)) +} + +// targstr returns a comma-separated string of the types in targs +func targstr(targs []types.Type) string { + var sb strings.Builder + if len(targs) > 0 { + sb.WriteString("[") + for i := range targs { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(targs[i].String()) + } + sb.WriteString("]") + } + return sb.String() +} + +// origin is the function that fn is an instantiation of. Returns nil if fn is +// not an instantiation. +// +// Precondition: fn and the origin function are done building. +func origin(fn *Function) *Function { + if fn.parent != nil && fn.parent.hasTypeArgs() { + return origin(fn.parent).AnonFuncs[fn.anonIdx] + } + return fn.topLevelOrigin +} func (v *Alloc) Type() types.Type { return v.typ } func (v *Alloc) Referrers() *[]Instruction { return &v.referrers } @@ -1879,6 +1975,11 @@ func (c *NamedConst) Object() types.Object { return c.object } func (c *NamedConst) Package() *Package { return c.pkg } func (c *NamedConst) RelString(from *types.Package) string { return relString(c, from) } +func (v *Function) Pos() token.Pos { return v.pos } +func (v *Function) Source() ast.Node { return v.syntax } + +func (d *debugRef) Object() types.Object { return d.object } + // Func returns the package-level function of the specified name, // or nil if not found. func (p *Package) Func(name string) (f *Function) { @@ -1907,7 +2008,7 @@ func (p *Package) Type(name string) (t *Type) { return } -func (s *DebugRef) Pos() token.Pos { return s.Expr.Pos() } +func (s *debugRef) Pos() token.Pos { return s.Expr.Pos() } // Operands. @@ -1936,7 +2037,7 @@ func (s *Call) Operands(rands []*Value) []*Value { } func (s *Defer) Operands(rands []*Value) []*Value { - return append(s.Call.Operands(rands), &s._DeferStack) + return append(s.Call.Operands(rands), &s.DeferStack) } func (v *ChangeInterface) Operands(rands []*Value) []*Value { @@ -1963,11 +2064,7 @@ func (v *SliceToArray) Operands(rands []*Value) []*Value { return append(rands, &v.X) } -func (s *DebugRef) Operands(rands []*Value) []*Value { - return append(rands, &s.X) -} - -func (s *Copy) Operands(rands []*Value) []*Value { +func (s *debugRef) Operands(rands []*Value) []*Value { return append(rands, &s.X) } @@ -2060,10 +2157,6 @@ func (s *Panic) Operands(rands []*Value) []*Value { return append(rands, &s.X) } -func (v *Sigma) Operands(rands []*Value) []*Value { - return append(rands, &v.X) -} - func (v *Phi) Operands(rands []*Value) []*Value { for i := range v.Edges { rands = append(rands, &v.Edges[i]) @@ -2140,11 +2233,9 @@ func (v *CompositeValue) Operands(rands []*Value) []*Value { } // Non-Instruction Values: -func (v *Builtin) Operands(rands []*Value) []*Value { return rands } -func (v *FreeVar) Operands(rands []*Value) []*Value { return rands } -func (v *Const) Operands(rands []*Value) []*Value { return rands } -func (v *ArrayConst) Operands(rands []*Value) []*Value { return rands } -func (v *GenericConst) Operands(rands []*Value) []*Value { return rands } -func (v *Function) Operands(rands []*Value) []*Value { return rands } -func (v *Global) Operands(rands []*Value) []*Value { return rands } -func (v *Parameter) Operands(rands []*Value) []*Value { return rands } +func (v *Builtin) Operands(rands []*Value) []*Value { return rands } +func (v *FreeVar) Operands(rands []*Value) []*Value { return rands } +func (v *Const) Operands(rands []*Value) []*Value { return rands } +func (v *Function) Operands(rands []*Value) []*Value { return rands } +func (v *Global) Operands(rands []*Value) []*Value { return rands } +func (v *Parameter) Operands(rands []*Value) []*Value { return rands } diff --git a/vendor/honnef.co/go/tools/go/ir/subst.go b/vendor/honnef.co/go/tools/go/ir/subst.go new file mode 100644 index 00000000..07b636a4 --- /dev/null +++ b/vendor/honnef.co/go/tools/go/ir/subst.go @@ -0,0 +1,577 @@ +// 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 ir + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/xtools-internal/aliases" +) + +// subster defines a type substitution operation of a set of type parameters +// to type parameter free replacement types. Substitution is done within +// the context of a package-level function instantiation. *Named types +// declared in the function are unique to the instantiation. +// +// For example, given a parameterized function F +// +// func F[S, T any]() any { +// type X struct{ s S; next *X } +// var p *X +// return p +// } +// +// calling the instantiation F[string, int]() returns an interface +// value (*X[string,int], nil) where the underlying value of +// X[string,int] is a struct{s string; next *X[string,int]}. +// +// A nil *subster is a valid, empty substitution map. It always acts as +// the identity function. This allows for treating parameterized and +// non-parameterized functions identically while compiling to ssa. +// +// Not concurrency-safe. +// +// Note: Some may find it helpful to think through some of the most +// complex substitution cases using lambda calculus inspired notation. +// subst.typ() solves evaluating a type expression E +// within the body of a function Fn[m] with the type parameters m +// once we have applied the type arguments N. +// We can succinctly write this as a function application: +// +// ((λm. E) N) +// +// go/types does not provide this interface directly. +// So what subster provides is a type substitution operation +// +// E[m:=N] +type subster struct { + replacements map[*types.TypeParam]types.Type // values should contain no type params + cache map[types.Type]types.Type // cache of subst results + origin *types.Func // types.Objects declared within this origin function are unique within this context + ctxt *types.Context // speeds up repeated instantiations + uniqueness typeutil.Map[types.Type] // determines the uniqueness of the instantiations within the function + // TODO(taking): consider adding Pos +} + +// Returns a subster that replaces rtparams[i] with rtargs[i] and tparams[i] with targs[i]. +// Uses ctxt as a cache. rtargs and targs should not contain any types in rtparams or tparams. +// fn is the generic function for which we are substituting. +func makeSubster(ctxt *types.Context, fn *types.Func, rtparams *types.TypeParamList, rtargs []types.Type, tparams *types.TypeParamList, targs []types.Type) *subster { + got := len(rtargs) + len(targs) + want := rtparams.Len() + tparams.Len() + if got != want { + panic(fmt.Sprintf("makeSubster argument count must match: got %d; want %d", got, want)) + } + + subst := &subster{ + replacements: make(map[*types.TypeParam]types.Type, want), + cache: make(map[types.Type]types.Type), + origin: fn.Origin(), + ctxt: ctxt, + } + for i := 0; i < rtparams.Len(); i++ { + subst.replacements[rtparams.At(i)] = rtargs[i] + } + for i := 0; i < tparams.Len(); i++ { + subst.replacements[tparams.At(i)] = targs[i] + } + return subst +} + +// typ returns the type of t with the type parameter tparams[i] substituted +// for the type targs[i] where subst was created using tparams and targs. +func (subst *subster) typ(t types.Type) (res types.Type) { + if subst == nil { + return t // A nil subst is type preserving. + } + if r, ok := subst.cache[t]; ok { + return r + } + defer func() { + subst.cache[t] = res + }() + + switch t := t.(type) { + case *types.TypeParam: + if r := subst.replacements[t]; r != nil { + return r + } + return t + + case *types.Basic: + return t + + case *types.Array: + if r := subst.typ(t.Elem()); r != t.Elem() { + return types.NewArray(r, t.Len()) + } + return t + + case *types.Slice: + if r := subst.typ(t.Elem()); r != t.Elem() { + return types.NewSlice(r) + } + return t + + case *types.Pointer: + if r := subst.typ(t.Elem()); r != t.Elem() { + return types.NewPointer(r) + } + return t + + case *types.Tuple: + return subst.tuple(t) + + case *types.Struct: + return subst.struct_(t) + + case *types.Map: + key := subst.typ(t.Key()) + elem := subst.typ(t.Elem()) + if key != t.Key() || elem != t.Elem() { + return types.NewMap(key, elem) + } + return t + + case *types.Chan: + if elem := subst.typ(t.Elem()); elem != t.Elem() { + return types.NewChan(t.Dir(), elem) + } + return t + + case *types.Signature: + return subst.signature(t) + + case *types.Union: + return subst.union(t) + + case *types.Interface: + return subst.interface_(t) + + case *types.Alias: + return subst.alias(t) + + case *types.Named: + return subst.named(t) + + case *typeutil.Iterator: + elem := subst.typ(t.Elem()) + if elem != t.Elem() { + return typeutil.NewIterator(elem) + } + return t + + case *typeutil.DeferStack: + return t + + default: + panic(fmt.Sprintf("internal error: unhandled type %T", t)) + } +} + +// types returns the result of {subst.typ(ts[i])}. +func (subst *subster) types(ts []types.Type) []types.Type { + res := make([]types.Type, len(ts)) + for i := range ts { + res[i] = subst.typ(ts[i]) + } + return res +} + +func (subst *subster) tuple(t *types.Tuple) *types.Tuple { + if t != nil { + if vars := subst.varlist(t); vars != nil { + return types.NewTuple(vars...) + } + } + return t +} + +type varlist interface { + At(i int) *types.Var + Len() int +} + +// fieldlist is an adapter for structs for the varlist interface. +type fieldlist struct { + str *types.Struct +} + +func (fl fieldlist) At(i int) *types.Var { return fl.str.Field(i) } +func (fl fieldlist) Len() int { return fl.str.NumFields() } + +func (subst *subster) struct_(t *types.Struct) *types.Struct { + if t != nil { + if fields := subst.varlist(fieldlist{t}); fields != nil { + tags := make([]string, t.NumFields()) + for i, n := 0, t.NumFields(); i < n; i++ { + tags[i] = t.Tag(i) + } + return types.NewStruct(fields, tags) + } + } + return t +} + +// varlist returns subst(in[i]) or return nils if subst(v[i]) == v[i] for all i. +func (subst *subster) varlist(in varlist) []*types.Var { + var out []*types.Var // nil => no updates + for i, n := 0, in.Len(); i < n; i++ { + v := in.At(i) + w := subst.var_(v) + if v != w && out == nil { + out = make([]*types.Var, n) + for j := 0; j < i; j++ { + out[j] = in.At(j) + } + } + if out != nil { + out[i] = w + } + } + return out +} + +func (subst *subster) var_(v *types.Var) *types.Var { + if v != nil { + if typ := subst.typ(v.Type()); typ != v.Type() { + if v.IsField() { + return types.NewField(v.Pos(), v.Pkg(), v.Name(), typ, v.Embedded()) + } + return types.NewParam(v.Pos(), v.Pkg(), v.Name(), typ) + } + } + return v +} + +func (subst *subster) union(u *types.Union) *types.Union { + var out []*types.Term // nil => no updates + + for i, n := 0, u.Len(); i < n; i++ { + t := u.Term(i) + r := subst.typ(t.Type()) + if r != t.Type() && out == nil { + out = make([]*types.Term, n) + for j := 0; j < i; j++ { + out[j] = u.Term(j) + } + } + if out != nil { + out[i] = types.NewTerm(t.Tilde(), r) + } + } + + if out != nil { + return types.NewUnion(out) + } + return u +} + +func (subst *subster) interface_(iface *types.Interface) *types.Interface { + if iface == nil { + return nil + } + + // 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 receivers. + var methods []*types.Func + initMethods := func(n int) { // copy first n explicit methods + methods = make([]*types.Func, iface.NumExplicitMethods()) + for i := range n { + f := iface.ExplicitMethod(i) + norecv := changeRecv(f.Type().(*types.Signature), nil) + methods[i] = types.NewFunc(f.Pos(), f.Pkg(), f.Name(), norecv) + } + } + 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 receivers + // that do not go through a Named. + norecv := changeRecv(f.Type().(*types.Signature), nil) + sig := subst.typ(norecv) + if sig != norecv && methods == nil { + initMethods(i) + } + if methods != nil { + methods[i] = types.NewFunc(f.Pos(), f.Pkg(), f.Name(), sig.(*types.Signature)) + } + } + + var embeds []types.Type + initEmbeds := func(n int) { // copy first n embedded types + embeds = make([]types.Type, iface.NumEmbeddeds()) + for i := range n { + embeds[i] = iface.EmbeddedType(i) + } + } + for i := 0; i < iface.NumEmbeddeds(); i++ { + e := iface.EmbeddedType(i) + r := subst.typ(e) + if e != r && embeds == nil { + initEmbeds(i) + } + if embeds != nil { + embeds[i] = r + } + } + + if methods == nil && embeds == nil { + return iface + } + if methods == nil { + initMethods(iface.NumExplicitMethods()) + } + if embeds == nil { + initEmbeds(iface.NumEmbeddeds()) + } + return types.NewInterfaceType(methods, embeds).Complete() +} + +func (subst *subster) alias(t *types.Alias) types.Type { + // See subster.named. This follows the same strategy. + tparams := t.TypeParams() + targs := t.TypeArgs() + tname := t.Obj() + torigin := t.Origin() + + if !declaredWithin(tname, subst.origin) { + // t is declared outside of the function origin. So t is a package level type alias. + if targs.Len() == 0 { + // No type arguments so no instantiation needed. + return t + } + + // Instantiate with the substituted type arguments. + newTArgs := subst.typelist(targs) + return subst.instantiate(torigin, newTArgs) + } + + if targs.Len() == 0 { + // t is declared within the function origin and has no type arguments. + // + // Example: This corresponds to A or B in F, but not A[int]: + // + // func F[T any]() { + // type A[S any] = struct{t T, s S} + // type B = T + // var x A[int] + // ... + // } + // + // This is somewhat different than *Named as *Alias cannot be created recursively. + + // Copy and substitute type params. + var newTParams []*types.TypeParam + for cur := range tparams.TypeParams() { + cobj := cur.Obj() + cname := types.NewTypeName(cobj.Pos(), cobj.Pkg(), cobj.Name(), nil) + ntp := types.NewTypeParam(cname, nil) + subst.cache[cur] = ntp // See the comment "Note: Subtle" in subster.named. + newTParams = append(newTParams, ntp) + } + + // Substitute rhs. + rhs := subst.typ(t.Rhs()) + + // Create the fresh alias. + obj := aliases.New(tname.Pos(), tname.Pkg(), tname.Name(), rhs, newTParams) + + // Substitute into all of the constraints after they are created. + for i, ntp := range newTParams { + bound := tparams.At(i).Constraint() + ntp.SetConstraint(subst.typ(bound)) + } + return obj.Type() + } + + // t is declared within the function origin and has type arguments. + // + // Example: This corresponds to A[int] in F. Cases A and B are handled above. + // func F[T any]() { + // type A[S any] = struct{t T, s S} + // type B = T + // var x A[int] + // ... + // } + subOrigin := subst.typ(torigin) + subTArgs := subst.typelist(targs) + return subst.instantiate(subOrigin, subTArgs) +} + +func (subst *subster) named(t *types.Named) types.Type { + // A Named type is a user defined type. + // Ignoring generics, Named types are canonical: they are identical if + // and only if they have the same defining symbol. + // Generics complicate things, both if the type definition itself is + // parameterized, and if the type is defined within the scope of a + // parameterized function. In this case, two named types are identical if + // and only if their identifying symbols are identical, and all type + // arguments bindings in scope of the named type definition (including the + // type parameters of the definition itself) are equivalent. + // + // Notably: + // 1. For type definition type T[P1 any] struct{}, T[A] and T[B] are identical + // only if A and B are identical. + // 2. Inside the generic func Fn[m any]() any { type T struct{}; return T{} }, + // the result of Fn[A] and Fn[B] have identical type if and only if A and + // B are identical. + // 3. Both 1 and 2 could apply, such as in + // func F[m any]() any { type T[x any] struct{}; return T{} } + // + // A subster replaces type parameters within a function scope, and therefore must + // also replace free type parameters in the definitions of local types. + // + // Note: There are some detailed notes sprinkled throughout that borrow from + // lambda calculus notation. These contain some over simplifying math. + // + // LC: One way to think about subster is that it is a way of evaluating + // ((λm. E) N) as E[m:=N]. + // Each Named type t has an object *TypeName within a scope S that binds an + // underlying type expression U. U can refer to symbols within S (+ S's ancestors). + // Let x = t.TypeParams() and A = t.TypeArgs(). + // Each Named type t is then either: + // U where len(x) == 0 && len(A) == 0 + // λx. U where len(x) != 0 && len(A) == 0 + // ((λx. U) A) where len(x) == len(A) + // In each case, we will evaluate t[m:=N]. + tparams := t.TypeParams() // x + targs := t.TypeArgs() // A + + if !declaredWithin(t.Obj(), subst.origin) { + // t is declared outside of Fn[m]. + // + // In this case, we can skip substituting t.Underlying(). + // The underlying type cannot refer to the type parameters. + // + // LC: Let free(E) be the set of free type parameters in an expression E. + // Then whenever m ∉ free(E), then E = E[m:=N]. + // t ∉ Scope(fn) so therefore m ∉ free(U) and m ∩ x = ∅. + if targs.Len() == 0 { + // t has no type arguments. So it does not need to be instantiated. + // + // This is the normal case in real Go code, where t is not parameterized, + // declared at some package scope, and m is a TypeParam from a parameterized + // function F[m] or method. + // + // LC: m ∉ free(A) lets us conclude m ∉ free(t). So t=t[m:=N]. + return t + } + + // t is declared outside of Fn[m] and has type arguments. + // The type arguments may contain type parameters m so + // substitute the type arguments, and instantiate the substituted + // type arguments. + // + // LC: Evaluate this as ((λx. U) A') where A' = A[m := N]. + newTArgs := subst.typelist(targs) + return subst.instantiate(t.Origin(), newTArgs) + } + + // t is declared within Fn[m]. + + if targs.Len() == 0 { // no type arguments? + assert(t == t.Origin(), "local parameterized type abstraction must be an origin type") + + // t has no type arguments. + // The underlying type of t may contain the function's type parameters, + // replace these, and create a new type. + // + // Subtle: We short circuit substitution and use a newly created type in + // subst, i.e. cache[t]=fresh, to preemptively replace t with fresh + // in recursive types during traversal. This both breaks infinite cycles + // and allows for constructing types with the replacement applied in + // subst.typ(U). + // + // A new copy of the Named and Typename (and constraints) per function + // instantiation matches the semantics of Go, which treats all function + // instantiations F[N] as having distinct local types. + // + // LC: x.Len()=0 can be thought of as a special case of λx. U. + // LC: Evaluate (λx. U)[m:=N] as (λx'. U') where U'=U[x:=x',m:=N]. + tname := t.Obj() + obj := types.NewTypeName(tname.Pos(), tname.Pkg(), tname.Name(), nil) + fresh := types.NewNamed(obj, nil, nil) + var newTParams []*types.TypeParam + for cur := range tparams.TypeParams() { + cobj := cur.Obj() + cname := types.NewTypeName(cobj.Pos(), cobj.Pkg(), cobj.Name(), nil) + ntp := types.NewTypeParam(cname, nil) + subst.cache[cur] = ntp + newTParams = append(newTParams, ntp) + } + fresh.SetTypeParams(newTParams) + subst.cache[t] = fresh + subst.cache[fresh] = fresh + fresh.SetUnderlying(subst.typ(t.Underlying())) + // Substitute into all of the constraints after they are created. + for i, ntp := range newTParams { + bound := tparams.At(i).Constraint() + ntp.SetConstraint(subst.typ(bound)) + } + return fresh + } + + // t is defined within Fn[m] and t has type arguments (an instantiation). + // We reduce this to the two cases above: + // (1) substitute the function's type parameters into t.Origin(). + // (2) substitute t's type arguments A and instantiate the updated t.Origin() with these. + // + // LC: Evaluate ((λx. U) A)[m:=N] as (t' A') where t' = (λx. U)[m:=N] and A'=A [m:=N] + subOrigin := subst.typ(t.Origin()) + subTArgs := subst.typelist(targs) + return subst.instantiate(subOrigin, subTArgs) +} + +func (subst *subster) instantiate(orig types.Type, targs []types.Type) types.Type { + i, err := types.Instantiate(subst.ctxt, orig, targs, false) + assert(err == nil, "failed to Instantiate named (Named or Alias) type") + if c, _ := subst.uniqueness.At(i); c != nil { + return c + } + subst.uniqueness.Set(i, i) + return i +} + +func (subst *subster) typelist(l *types.TypeList) []types.Type { + res := make([]types.Type, l.Len()) + for i := 0; i < l.Len(); i++ { + res[i] = subst.typ(l.At(i)) + } + return res +} + +func (subst *subster) signature(t *types.Signature) types.Type { + tparams := t.TypeParams() + + // We are choosing not to support tparams.Len() > 0 until a need has been observed in practice. + // + // There are some known usages for types.Types coming from types.{Eval,CheckExpr}. + // To support tparams.Len() > 0, we just need to do the following [pseudocode]: + // targs := {subst.replacements[tparams[i]]]}; Instantiate(ctxt, t, targs, false) + + assert(tparams.Len() == 0, "Substituting types.Signatures with generic functions are currently unsupported.") + + // Either: + // (1)non-generic function. + // no type params to substitute + // (2)generic method and recv needs to be substituted. + + // Receivers can be either: + // named + // pointer to named + // interface + // nil + // interface is the problematic case. We need to cycle break there! + recv := subst.var_(t.Recv()) + params := subst.tuple(t.Params()) + results := subst.tuple(t.Results()) + if recv != t.Recv() || params != t.Params() || results != t.Results() { + return types.NewSignatureType(recv, nil, nil, params, results, t.Variadic()) + } + return t +} diff --git a/vendor/honnef.co/go/tools/go/ir/task.go b/vendor/honnef.co/go/tools/go/ir/task.go new file mode 100644 index 00000000..7a44404b --- /dev/null +++ b/vendor/honnef.co/go/tools/go/ir/task.go @@ -0,0 +1,103 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ir + +import ( + "sync/atomic" +) + +// Each task has two states: it is initially "active", +// and transitions to "done". +// +// tasks form a directed graph. An edge from x to y (with y in x.edges) +// indicates that the task x waits on the task y to be done. +// Cycles are permitted. +// +// Calling x.wait() blocks the calling goroutine until task x, +// and all the tasks transitively reachable from x are done. +// +// The nil *task is always considered done. +type task struct { + done chan unit // close when the task is done. + edges map[*task]unit // set of predecessors of this task. + transitive atomic.Bool // true once it is known all predecessors are done. +} + +func (x *task) isTransitivelyDone() bool { return x == nil || x.transitive.Load() } + +// addEdge creates an edge from x to y, indicating that +// x.wait() will not return before y is done. +// All calls to x.addEdge(...) should happen before x.markDone(). +func (x *task) addEdge(y *task) { + if x == y || y.isTransitivelyDone() { + return // no work remaining + } + + // heuristic done check + select { + case <-x.done: + panic("cannot add an edge to a done task") + default: + } + + if x.edges == nil { + x.edges = make(map[*task]unit) + } + x.edges[y] = unit{} +} + +// markDone changes the task's state to markDone. +func (x *task) markDone() { + if x != nil { + close(x.done) + } +} + +// wait blocks until x and all the tasks it can reach through edges are done. +func (x *task) wait() { + if x.isTransitivelyDone() { + return // already known to be done. Skip allocations. + } + + // Use BFS to wait on u.done to be closed, for all u transitively + // reachable from x via edges. + // + // This work can be repeated by multiple workers doing wait(). + // + // Note: Tarjan's SCC algorithm is able to mark SCCs as transitively done + // as soon as the SCC has been visited. This is theoretically faster, but is + // a more complex algorithm. Until we have evidence, we need the more complex + // algorithm, the simpler algorithm BFS is implemented. + // + // In Go 1.23, ssa/TestStdlib reaches <=3 *tasks per wait() in most schedules + // On some schedules, there is a cycle building net/http and internal/trace/testtrace + // due to slices functions. + work := []*task{x} + enqueued := map[*task]unit{x: {}} + for i := 0; i < len(work); i++ { + u := work[i] + if u.isTransitivelyDone() { // already transitively done + work[i] = nil + continue + } + <-u.done // wait for u to be marked done. + + for v := range u.edges { + if _, ok := enqueued[v]; !ok { + enqueued[v] = unit{} + work = append(work, v) + } + } + } + + // work is transitively closed over dependencies. + // u in work is done (or transitively done and skipped). + // u is transitively done. + for _, u := range work { + if u != nil { + x.transitive.Store(true) + } + } +} diff --git a/vendor/honnef.co/go/tools/go/ir/typeset.go b/vendor/honnef.co/go/tools/go/ir/typeset.go new file mode 100644 index 00000000..6154e4f1 --- /dev/null +++ b/vendor/honnef.co/go/tools/go/ir/typeset.go @@ -0,0 +1,175 @@ +// 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 ir + +import ( + "go/types" + + "honnef.co/go/tools/go/types/typeutil" +) + +// Utilities for dealing with type sets. + +const debug = false + +// typeset is an iterator over the (type/underlying type) pairs of the +// specific type terms of the type set implied by t. +// If t is a type parameter, the implied type set is the type set of t's constraint. +// In that case, if there are no specific terms, typeset calls yield with (nil, nil). +// If t is not a type parameter, the implied type set consists of just t. +// In any case, typeset is guaranteed to call yield at least once. +func typeset(typ types.Type, yield func(t, u types.Type) bool) { + switch typ := types.Unalias(typ).(type) { + case *types.TypeParam, *types.Interface: + terms := termListOf(typ) + if len(terms) == 0 { + yield(nil, nil) + return + } + for _, term := range terms { + u := types.Unalias(term.Type()) + if !term.Tilde() { + u = u.Underlying() + } + if debug { + assert(types.Identical(u, u.Underlying()), "Unalias(x) == under(x) for ~x terms") + } + if !yield(term.Type(), u) { + break + } + } + return + default: + yield(typ, typ.Underlying()) + } +} + +// termListOf returns the type set of typ as a normalized term set. Returns an empty set on an error. +func termListOf(typ types.Type) []*types.Term { + return typeutil.NewTypeSet(typ).Terms +} + +// typeSetIsEmpty returns true if a typeset is empty. +func typeSetIsEmpty(typ types.Type) bool { + var empty bool + typeset(typ, func(t, _ types.Type) bool { + empty = t == nil + return false + }) + return empty +} + +// isBytestring returns true if T has the same terms as interface{[]byte | string}. +// These act like a core type for some operations: slice expressions, append and copy. +// +// See https://go.dev/ref/spec#Core_types for the details on bytestring. +func isBytestring(T types.Type) bool { + U := T.Underlying() + if _, ok := U.(*types.Interface); !ok { + return false + } + + hasBytes, hasString := false, false + ok := underIs(U, func(t types.Type) bool { + switch { + case isString(t): + hasString = true + return true + case isByteSlice(t): + hasBytes = true + return true + default: + return false + } + }) + return ok && hasBytes && hasString +} + +// underIs calls f with the underlying types of the type terms +// of the type set of typ and reports whether all calls to f returned true. +// If there are no specific terms, underIs returns the result of f(nil). +func underIs(typ types.Type, f func(types.Type) bool) bool { + var ok bool + typeset(typ, func(t, u types.Type) bool { + ok = f(u) + return ok + }) + return ok +} + +// indexType returns the element type and index mode of a IndexExpr over a type. +// It returns an invalid mode if the type is not indexable; this should never occur in a well-typed program. +func indexType(typ types.Type) (types.Type, indexMode) { + switch U := typ.Underlying().(type) { + case *types.Array: + return U.Elem(), ixArrVar + case *types.Pointer: + if arr, ok := U.Elem().Underlying().(*types.Array); ok { + return arr.Elem(), ixVar + } + case *types.Slice: + return U.Elem(), ixVar + case *types.Map: + return U.Elem(), ixMap + case *types.Basic: + return tByte, ixValue // must be a string + case *types.Interface: + var elem types.Type + mode := ixInvalid + typeset(typ, func(t, _ types.Type) bool { + if t == nil { + return false // empty set + } + e, m := indexType(t) + if elem == nil { + elem, mode = e, m + } + if debug && !types.Identical(elem, e) { // if type checked, just a sanity check + mode = ixInvalid + return false + } + // Update the mode to the most constrained address type. + mode = mode.meet(m) + return mode != ixInvalid + }) + return elem, mode + } + return nil, ixInvalid +} + +// An indexMode specifies the (addressing) mode of an index operand. +// +// Addressing mode of an index operation is based on the set of +// underlying types. +// Hasse diagram of the indexMode meet semi-lattice: +// +// ixVar ixMap +// | | +// ixArrVar | +// | | +// ixValue | +// \ / +// ixInvalid +type indexMode byte + +const ( + ixInvalid indexMode = iota // index is invalid + ixValue // index is a computed value (not addressable) + ixArrVar // like ixVar, but index operand contains an array + ixVar // index is an addressable variable + ixMap // index is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment) +) + +// meet is the address type that is constrained by both x and y. +func (x indexMode) meet(y indexMode) indexMode { + if (x == ixMap || y == ixMap) && x != y { + return ixInvalid + } + // Use int representation and return min. + if x < y { + return y + } + return x +} diff --git a/vendor/honnef.co/go/tools/go/ir/util.go b/vendor/honnef.co/go/tools/go/ir/util.go index 97fe9c5f..d845c9a1 100644 --- a/vendor/honnef.co/go/tools/go/ir/util.go +++ b/vendor/honnef.co/go/tools/go/ir/util.go @@ -13,16 +13,17 @@ import ( "go/types" "io" "os" + "sync" + _ "unsafe" // for go:linkname hack - "honnef.co/go/tools/go/ast/astutil" "honnef.co/go/tools/go/types/typeutil" "golang.org/x/exp/typeparams" ) -//// AST utilities +type unit struct{} -func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) } +//// AST utilities // isBlankIdent returns true iff e is an Ident with name "_". // They have no associated types.Object, and thus no type. @@ -33,14 +34,66 @@ 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, -// and for type parameters whose core type is a pointer. -func isPointer(typ types.Type) bool { - if ctyp := typeutil.CoreType(typ); ctyp != nil { - _, ok := ctyp.(*types.Pointer) - return ok +// isString reports whether t is exactly a string type. +// t is assumed to be an Underlying type (not Named or Alias). +func isString(t types.Type) bool { + basic, ok := t.(*types.Basic) + return ok && basic.Info()&types.IsString != 0 +} + +// isByteSlice reports whether t is of the form []~bytes. +// t is assumed to be an Underlying type (not Named or Alias). +func isByteSlice(t types.Type) bool { + if b, ok := t.(*types.Slice); ok { + e, _ := b.Elem().Underlying().(*types.Basic) + return e != nil && e.Kind() == types.Byte + } + return false +} + +// isRuneSlice reports whether t is of the form []~runes. +// t is assumed to be an Underlying type (not Named or Alias). +func isRuneSlice(t types.Type) bool { + if b, ok := t.(*types.Slice); ok { + e, _ := b.Elem().Underlying().(*types.Basic) + return e != nil && e.Kind() == types.Rune } - _, ok := typ.Underlying().(*types.Pointer) + return false +} + +// isBasicConvTypes returns true when the type set of a type +// can be one side of a Convert operation. This is when: +// - All are basic, []byte, or []rune. +// - At least 1 is basic. +// - At most 1 is []byte or []rune. +func isBasicConvTypes(typ types.Type) bool { + basics, cnt := 0, 0 + ok := underIs(typ, func(t types.Type) bool { + cnt++ + if isBasic(t) { + basics++ + return true + } + return isByteSlice(t) || isRuneSlice(t) + }) + return ok && basics >= 1 && cnt-basics <= 1 +} + +// isPointer reports whether t's underlying type is a pointer. +func isPointer(t types.Type) bool { + return is[*types.Pointer](t.Underlying()) +} + +// isPointerCore reports whether t's core type is a pointer. +// +// (Most pointer manipulation is related to receivers, in which case +// isPointer is appropriate. tecallers can use isPointer(t). +func isPointerCore(t types.Type) bool { + return is[*types.Pointer](typeutil.CoreType(t)) +} + +func is[T any](x any) bool { + _, ok := x.(T) return ok } @@ -67,6 +120,44 @@ 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 := typeutil.CoreType(typ).(*types.Struct); ok { + if 0 <= index && index < st.NumFields() { + return st.Field(index) + } + } + return nil +} + +// isUntyped reports whether typ is the type of an untyped constant. +func isUntyped(typ types.Type) bool { + // No Underlying/Unalias: untyped constant types cannot be Named or Alias. + b, ok := typ.(*types.Basic) + return ok && b.Info()&types.IsUntyped != 0 +} + +// declaredWithin reports whether an object is declared within a function. +// +// obj must not be a method or a field. +func declaredWithin(obj types.Object, fn *types.Func) bool { + if obj.Pos() != token.NoPos { + return fn.Scope().Contains(obj.Pos()) // trust the positions if they exist. + } + if fn.Pkg() != obj.Pkg() { + return false // fast path for different packages + } + + // Traverse Parent() scopes for fn.Scope(). + for p := obj.Parent(); p != nil; p = p.Parent() { + if p == fn.Scope() { + return true + } + } + return false +} + // logStack prints the formatted "start" message to stderr and // returns a closure that prints the corresponding "end" message. // Call using 'defer logStack(...)()' to show builder stack on panic. @@ -102,61 +193,244 @@ func makeLen(T types.Type) *Builtin { } } -type StackMap struct { - m []map[Value]Value +func assert(x bool, msg string) { + if !x { + panic(msg) + } } -func (m *StackMap) Push() { - m.m = append(m.m, map[Value]Value{}) -} +// BlockMap is a mapping from basic blocks (identified by their indices) to values. +type BlockMap[T any] []T -func (m *StackMap) Pop() { - m.m = m.m[:len(m.m)-1] +// isBasic reports whether t is a basic type. +func isBasic(t types.Type) bool { + _, ok := t.(*types.Basic) + return ok } -func (m *StackMap) Get(key Value) (Value, bool) { - for i := len(m.m) - 1; i >= 0; i-- { - if v, ok := m.m[i][key]; ok { - return v, true +// receiverTypeArgs returns the type arguments to a method's receiver. +// Returns an empty list if the receiver does not have type arguments. +func receiverTypeArgs(method *types.Func) []types.Type { + recv := method.Signature().Recv() + named := func() (named *types.Named) { + t := recv.Type() + if ptr, ok := types.Unalias(t).(*types.Pointer); ok { + t = ptr.Elem() } + named, _ = types.Unalias(t).(*types.Named) + return named + }() + if named == nil { + return nil // recv is anonymous struct/interface + } + ts := named.TypeArgs() + if ts.Len() == 0 { + return nil + } + targs := make([]types.Type, ts.Len()) + for i := 0; i < ts.Len(); i++ { + targs[i] = ts.At(i) + } + return targs +} + +// recvAsFirstArg takes a method signature and returns a function +// signature with receiver as the first parameter. +func recvAsFirstArg(sig *types.Signature) *types.Signature { + params := make([]*types.Var, 0, 1+sig.Params().Len()) + params = append(params, sig.Recv()) + for v := range sig.Params().Variables() { + params = append(params, v) + } + return types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic()) +} + +// instance returns whether an expression is a simple or qualified identifier +// that is a generic instantiation. +func instance(info *types.Info, expr ast.Expr) bool { + // Compare the logic here against go/types.instantiatedIdent, + // which also handles *IndexExpr and *IndexListExpr. + var id *ast.Ident + switch x := expr.(type) { + case *ast.Ident: + id = x + case *ast.SelectorExpr: + id = x.Sel + default: + return false + } + _, ok := info.Instances[id] + return ok +} + +// 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) +} + +// instanceArgs returns the Instance[id].TypeArgs as a slice. +func instanceArgs(info *types.Info, id *ast.Ident) []types.Type { + targList := info.Instances[id].TypeArgs + if targList == nil { + return nil + } + + targs := make([]types.Type, targList.Len()) + for i, n := 0, targList.Len(); i < n; i++ { + targs[i] = targList.At(i) } - return nil, false + return targs } -func (m *StackMap) Set(k Value, v Value) { - m.m[len(m.m)-1][k] = v +// Mapping of a type T to a canonical instance C s.t. types.Identical(T, C). +// Thread-safe. +type canonizer struct { + mu sync.Mutex + types typeutil.Map[types.Type] // map from type to a canonical instance + lists typeListMap // map from a list of types to a canonical instance } -// Unwrap recursively unwraps Sigma and Copy nodes. -func Unwrap(v Value) Value { - for { - switch vv := v.(type) { - case *Sigma: - v = vv.X - case *Copy: - v = vv.X - default: - return v +func newCanonizer() *canonizer { + c := &canonizer{} + h := typeutil.MakeHasher() + c.types.SetHasher(h) + c.lists.hasher = h + return c +} + +// List returns a canonical representative of a list of types. +// Representative of the empty list is nil. +func (c *canonizer) List(ts []types.Type) *typeList { + if len(ts) == 0 { + return nil + } + + unaliasAll := func(ts []types.Type) []types.Type { + // Is there some top level alias? + var found bool + for _, t := range ts { + if _, ok := t.(*types.Alias); ok { + found = true + break + } + } + if !found { + return ts // no top level alias } + + cp := make([]types.Type, len(ts)) // copy with top level aliases removed. + for i, t := range ts { + cp[i] = types.Unalias(t) + } + return cp } + l := unaliasAll(ts) + + c.mu.Lock() + defer c.mu.Unlock() + return c.lists.rep(l) } -func assert(x bool) { - if !x { - panic("failed assertion") +// Type returns a canonical representative of type T. +// Removes top-level aliases. +// +// For performance, reasons the canonical instance is order-dependent, +// and may contain deeply nested aliases. +func (c *canonizer) Type(T types.Type) types.Type { + T = types.Unalias(T) // remove the top level alias. + + c.mu.Lock() + defer c.mu.Unlock() + + if r, _ := c.types.At(T); r != nil { + return r } + c.types.Set(T, T) + return T } -// BlockMap is a mapping from basic blocks (identified by their indices) to values. -type BlockMap[T any] []T +// A type for representing a canonized list of types. +type typeList []types.Type -// isBasic reports whether t is a basic type. -func isBasic(t types.Type) bool { - _, ok := t.(*types.Basic) - return ok +func (l *typeList) identical(ts []types.Type) bool { + if l == nil { + return len(ts) == 0 + } + n := len(*l) + if len(ts) != n { + return false + } + for i, left := range *l { + right := ts[i] + if !types.Identical(left, right) { + return false + } + } + return true } -// 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) +type typeListMap struct { + hasher typeutil.Hasher + buckets map[uint32][]*typeList } + +// rep returns a canonical representative of a slice of types. +func (m *typeListMap) rep(ts []types.Type) *typeList { + if m == nil || len(ts) == 0 { + return nil + } + + if m.buckets == nil { + m.buckets = make(map[uint32][]*typeList) + } + + h := m.hash(ts) + bucket := m.buckets[h] + for _, l := range bucket { + if l.identical(ts) { + return l + } + } + + // not present. create a representative. + cp := make(typeList, len(ts)) + copy(cp, ts) + rep := &cp + + m.buckets[h] = append(bucket, rep) + return rep +} + +func (m *typeListMap) hash(ts []types.Type) uint32 { + if m == nil { + return 0 + } + // Some smallish prime far away from typeutil.Hash. + n := len(ts) + h := uint32(13619) + 2*uint32(n) + for i := range n { + h += 3 * m.hasher.Hash(ts[i]) + } + return h +} + +// instantiateMethod instantiates m with targs and returns a canonical representative for this method. +func (canon *canonizer) instantiateMethod(m *types.Func, targs []types.Type, ctxt *types.Context) *types.Func { + recv := recvType(m) + if p, ok := types.Unalias(recv).(*types.Pointer); ok { + recv = p.Elem() + } + named := types.Unalias(recv).(*types.Named) + inst, err := types.Instantiate(ctxt, named.Origin(), targs, false) + if err != nil { + panic(err) + } + rep := canon.Type(inst) + obj, _, _ := types.LookupFieldOrMethod(rep, true, m.Pkg(), m.Name()) + return obj.(*types.Func) +} + +// Exposed to irutil using the linkname hack. +// +//go:linkname isSyntactic honnef.co/go/tools/go/ir.isSyntactic +func isSyntactic(pkg *Package) bool { return pkg.syntax } diff --git a/vendor/honnef.co/go/tools/go/ir/wrappers.go b/vendor/honnef.co/go/tools/go/ir/wrappers.go index 6b358a7c..1d7b3e48 100644 --- a/vendor/honnef.co/go/tools/go/ir/wrappers.go +++ b/vendor/honnef.co/go/tools/go/ir/wrappers.go @@ -26,7 +26,7 @@ import ( // -- wrappers ----------------------------------------------------------- -// makeWrapper returns a synthetic method that delegates to the +// createWrapper returns a synthetic method that delegates to the // declared method denoted by meth.Obj(), first performing any // necessary pointer indirections or field selections implied by meth. // @@ -36,65 +36,92 @@ import ( // following axes of variation when making changes: // - optional receiver indirection // - optional implicit field selections +// - optional method type arguments // - meth.Obj() may denote a concrete or an interface method // - the result may be a thunk or a wrapper. -// -// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) -func makeWrapper(prog *Program, sel *types.Selection) *Function { - obj := sel.Obj().(*types.Func) // the declared function - sig := sel.Type().(*types.Signature) // type of this wrapper +func createWrapper(prog *Program, sel *selection, targs []types.Type) *Function { + obj := sel.obj.(*types.Func) // the declared function + name, sig := maybeInstance(prog, obj.Name(), sel.typ.(*types.Signature), targs) var recv *types.Var // wrapper's receiver or thunk's params[0] - name := obj.Name() - var description Synthetic - var start int // first regular param - if sel.Kind() == types.MethodExpr { + var description string + if sel.kind == types.MethodExpr { name += "$thunk" - description = SyntheticThunk + description = "thunk" recv = sig.Params().At(0) - start = 1 } else { - description = SyntheticWrapper + description = "wrapper" recv = sig.Recv() } + description = fmt.Sprintf("%s for %s", description, obj) if prog.mode&LogSource != 0 { - defer logStack("make %s to (%s)", description, recv.Type())() + defer logStack("create %s to (%s)", description, recv.Type())() } - fn := &Function{ - name: name, - method: sel, - object: obj, - Signature: sig, - Synthetic: description, - Prog: prog, - functionBody: new(functionBody), + /* method wrapper */ + return &Function{ + name: name, + method: sel, + object: obj, + Signature: sig, + Synthetic: description, + Prog: prog, + pos: obj.Pos(), + typeargs: targs, + // wrappers have no syntax + build: (*builder).buildWrapper, + info: nil, + goversion: "", + } +} + +// maybeInstance returns name and sig instantiated to reflect any type arguments in targs. +func maybeInstance(prog *Program, name string, sig *types.Signature, targs []types.Type) (string, *types.Signature) { + if len(targs) > 0 { + name = fmt.Sprintf("%s%s", name, targstr(targs)) + instSig, err := types.Instantiate(prog.ctxt, sig, targs, false) + if err != nil { + // validate was false, we should never get an error + panic(err) + } + sig = prog.canon.Type(instSig).(*types.Signature) + } + return name, sig +} + +// buildWrapper builds fn.Body for a method wrapper. +func (b *builder) buildWrapper(fn *Function) { + var recv *types.Var // wrapper's receiver or thunk's params[0] + var start int // first regular param + if fn.method.kind == types.MethodExpr { + recv = fn.Signature.Params().At(0) + start = 1 + } else { + recv = fn.Signature.Recv() } - fn.initHTML(prog.PrintFunc) + fn.startBody() fn.addSpilledParam(recv, nil) createParams(fn, start) - indices := sel.Index() + indices := fn.method.index var v Value = fn.Locals[0] // spilled receiver - if isPointer(sel.Recv()) { + if isPointer(fn.method.recv) { v = emitLoad(fn, v, nil) // 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)) { + if len(indices) == 1 && !isPointer(recvType(fn.object)) { var c Call c.Call.Value = &Builtin{ - name: "ir:wrapnilchk", - sig: types.NewSignatureType(nil, nil, nil, - types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)), - types.NewTuple(anonVar(sel.Recv())), false), + name: "ssa:wrapnilchk", + sig: types.NewSignatureType(nil, nil, nil, types.NewTuple(anonVar(fn.method.recv), anonVar(tString), anonVar(tString)), types.NewTuple(anonVar(fn.method.recv)), false), } c.Call.Args = []Value{ v, - emitConst(fn, stringConst(deref(sel.Recv()).String(), nil)), - emitConst(fn, stringConst(sel.Obj().Name(), nil)), + stringConst(deref(fn.method.recv).String(), nil), + stringConst(fn.method.obj.Name(), nil), } c.setType(v.Type()) v = fn.emit(&c, nil) @@ -116,22 +143,21 @@ func makeWrapper(prog *Program, sel *types.Selection) *Function { // address of implicit C field. var c Call - if r := recvType(obj); !types.IsInterface(r) { // concrete method + if r := recvType(fn.object); !types.IsInterface(r) { // concrete method if !isPointer(r) { v = emitLoad(fn, v, nil) } - c.Call.Value = prog.declaredFunc(obj) + c.Call.Value = fn.Prog.objectMethod(fn.object, fn.typeargs, b) c.Call.Args = append(c.Call.Args, v) } else { - c.Call.Method = obj - c.Call.Value = emitLoad(fn, v, nil) + c.Call.Method = fn.object + c.Call.Value = emitLoad(fn, v, nil) // interface (possibly a typeparam) } for _, arg := range fn.Params[1:] { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c, nil) fn.finishBody() - return fn } // createParams creates parameters for wrapper method fn based on its @@ -146,7 +172,7 @@ func createParams(fn *Function, start int) { // -- bounds ----------------------------------------------------------- -// makeBound returns a bound method wrapper (or "bound"), a synthetic +// createBound returns a bound method wrapper (or "bound"), a synthetic // function that delegates to a concrete or interface method denoted // by obj. The resulting function has no receiver, but has one free // variable which will be used as the method's receiver in the @@ -165,56 +191,63 @@ func createParams(fn *Function, start int) { // // f := func() { return t.meth() } // -// Unlike makeWrapper, makeBound need perform no indirection or field +// Unlike createWrapper, createBound need perform no indirection or field // selections because that can be done before the closure is // constructed. -// -// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) -func makeBound(prog *Program, obj *types.Func) *Function { - prog.methodsMu.Lock() - defer prog.methodsMu.Unlock() +func createBound(prog *Program, obj *types.Func, targs []types.Type) *Function { + description := fmt.Sprintf("bound method wrapper for %s", obj) if prog.mode&LogSource != 0 { - defer logStack("%s", SyntheticBound)() + defer logStack("%s", description)() } + name, sig := maybeInstance(prog, obj.Name(), obj.Type().(*types.Signature), targs) + + /* bound method wrapper */ fn := &Function{ - name: obj.Name() + "$bound", - object: obj, - Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver - Synthetic: SyntheticBound, - Prog: prog, - functionBody: new(functionBody), + name: name + "$bound", + object: obj, + Signature: changeRecv(sig, nil), // drop receiver + Synthetic: description, + Prog: prog, + pos: obj.Pos(), + typeargs: targs, + // wrappers have no syntax + build: (*builder).buildBound, + info: nil, + goversion: "", } - fn.initHTML(prog.PrintFunc) + fn.FreeVars = []*FreeVar{{name: "recv", typ: recvType(obj), parent: fn}} // (cyclic) + return fn +} - fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn} - fn.FreeVars = []*FreeVar{fv} +// buildBound builds fn.Body for a bound method closure. +func (b *builder) buildBound(fn *Function) { fn.startBody() createParams(fn, 0) var c Call - if !types.IsInterface(recvType(obj)) { // concrete - c.Call.Value = prog.declaredFunc(obj) - c.Call.Args = []Value{fv} + recv := fn.FreeVars[0] + if !types.IsInterface(recvType(fn.object)) { // concrete + c.Call.Value = fn.Prog.objectMethod(fn.object, fn.typeargs, b) + c.Call.Args = []Value{recv} } else { - c.Call.Value = fv - c.Call.Method = obj + c.Call.Method = fn.object + c.Call.Value = recv // interface (possibly a typeparam) } for _, arg := range fn.Params { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c, nil) fn.finishBody() - return fn } // -- thunks ----------------------------------------------------------- -// makeThunk returns a thunk, a synthetic function that delegates to a -// concrete or interface method denoted by sel.Obj(). The resulting +// createThunk returns a thunk, a synthetic function that delegates to a +// concrete or interface method denoted by sel.obj. The resulting // function has no receiver, but has an additional (first) regular // parameter. // -// Precondition: sel.Kind() == types.MethodExpr. +// Precondition: sel.kind == types.MethodExpr. // // type T int or: type T interface { meth() } // func (t T) meth() @@ -225,17 +258,12 @@ func makeBound(prog *Program, obj *types.Func) *Function { // f is a synthetic wrapper defined as if by: // // f := func(t T) { return t.meth() } -// -// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) -func makeThunk(prog *Program, sel *types.Selection) *Function { - if sel.Kind() != types.MethodExpr { +func createThunk(prog *Program, sel *selection, targs []types.Type) *Function { + if sel.kind != types.MethodExpr { panic(sel) } - prog.methodsMu.Lock() - defer prog.methodsMu.Unlock() - - fn := makeWrapper(prog, sel) + fn := createWrapper(prog, sel, targs) if fn.Signature.Recv() != nil { panic(fn) // unexpected receiver } @@ -246,97 +274,84 @@ func changeRecv(s *types.Signature, recv *types.Var) *types.Signature { return types.NewSignatureType(recv, nil, nil, s.Params(), s.Results(), s.Variadic()) } -// makeInstance creates a wrapper function with signature sig that calls the generic function fn. -// If targs is not nil, fn is a function and targs describes the concrete type arguments. -// If targs is nil, fn is a method and the type arguments are derived from the receiver. -func makeInstance(prog *Program, fn *Function, sig *types.Signature, targs *types.TypeList) *Function { - if sig.Recv() != nil { - assert(targs == nil) - // Methods don't have their own type parameters, but the receiver does - targs = types.Unalias(deref(sig.Recv().Type())).(*types.Named).TypeArgs() - } else { - assert(targs != nil) - } +// A local version of *types.Selection. +// Needed for some additional control, such as creating a MethodExpr for an instantiation. +type selection struct { + kind types.SelectionKind + recv types.Type + typ types.Type + obj types.Object + index []int + indirect bool +} - wrapper := fn.generics.At(targs) - if wrapper != nil { - return wrapper +func toSelection(sel *types.Selection) *selection { + return &selection{ + kind: sel.Kind(), + recv: sel.Recv(), + typ: sel.Type(), + obj: sel.Obj(), + index: sel.Index(), + indirect: sel.Indirect(), } +} - var name string - if sig.Recv() != nil { - name = fn.name - } else { - name = fmt.Sprintf("%s$generic#%d", fn.name, fn.generics.Len()) - } - w := &Function{ - name: name, - object: fn.object, - Signature: sig, - Synthetic: SyntheticGeneric, - Prog: prog, - functionBody: new(functionBody), - } - w.initHTML(prog.PrintFunc) - w.startBody() +// -- instantiations -------------------------------------------------- + +// buildInstantiationWrapper builds the body of an instantiation +// wrapper fn. The body calls the original generic function, +// bracketed by ChangeType conversions on its arguments and results. +func (b *builder) buildInstantiationWrapper(fn *Function) { + orig := fn.topLevelOrigin + sig := fn.Signature + + fn.startBody() if sig.Recv() != nil { - w.addParamVar(sig.Recv(), nil) + fn.addParamVar(sig.Recv(), nil) } - createParams(w, 0) + createParams(fn, 0) + + // Create body. Add a call to origin generic function + // and make type changes between argument and parameters, + // as well as return values. var c Call - c.Call.Value = fn - tresults := fn.Signature.Results() - if tresults.Len() == 1 { - c.typ = tresults.At(0).Type() + c.Call.Value = orig + if res := orig.Signature.Results(); res.Len() == 1 { + c.typ = res.At(0).Type() } else { - c.typ = tresults - } - - changeType := func(v Value, typ types.Type) Value { - if types.Identical(v.Type(), typ) { - return v - } - var c ChangeType - c.X = v - c.typ = typ - return w.emit(&c, nil) + c.typ = res } - for i, arg := range w.Params { - if sig.Recv() != nil { - if i == 0 { - c.Call.Args = append(c.Call.Args, changeType(w.Params[0], fn.Signature.Recv().Type())) - } else { - c.Call.Args = append(c.Call.Args, changeType(arg, fn.Signature.Params().At(i-1).Type())) - } + // parameter of instance becomes an argument to the call + // to the original generic function. + argOffset := 0 + for i, arg := range fn.Params { + var typ types.Type + if i == 0 && sig.Recv() != nil { + typ = orig.Signature.Recv().Type() + argOffset = 1 } else { - c.Call.Args = append(c.Call.Args, changeType(arg, fn.Signature.Params().At(i).Type())) + typ = orig.Signature.Params().At(i - argOffset).Type() } + c.Call.Args = append(c.Call.Args, emitTypeCoercion(fn, arg, typ, nil)) } - for arg := range targs.Types() { - c.Call.TypeArgs = append(c.Call.TypeArgs, arg) - } - results := w.emit(&c, nil) + + results := fn.emit(&c, nil) var ret Return - switch tresults.Len() { + switch res := sig.Results(); res.Len() { case 0: + // no results, do nothing. case 1: - ret.Results = []Value{changeType(results, sig.Results().At(0).Type())} + ret.Results = []Value{emitTypeCoercion(fn, results, res.At(0).Type(), nil)} default: - for i := 0; i < tresults.Len(); i++ { - v := emitExtract(w, results, i, nil) - ret.Results = append(ret.Results, changeType(v, sig.Results().At(i).Type())) + for i := 0; i < sig.Results().Len(); i++ { + v := emitExtract(fn, results, i, nil) + ret.Results = append(ret.Results, emitTypeCoercion(fn, v, res.At(i).Type(), nil)) } } - w.Exit = w.newBasicBlock("exit") - emitJump(w, w.Exit, nil) - w.currentBlock = w.Exit - w.emit(&ret, nil) - w.currentBlock = nil + fn.emit(&ret, nil) + fn.currentBlock = nil - w.finishBody() - - fn.generics.Set(targs, w) - return w + fn.finishBody() } diff --git a/vendor/honnef.co/go/tools/go/ir/write.go b/vendor/honnef.co/go/tools/go/ir/write.go deleted file mode 100644 index 139c8cf3..00000000 --- a/vendor/honnef.co/go/tools/go/ir/write.go +++ /dev/null @@ -1,5 +0,0 @@ -package ir - -func NewJump(parent *BasicBlock) *Jump { - return &Jump{anInstruction{block: parent}} -} diff --git a/vendor/honnef.co/go/tools/go/types/typeutil/typeparams.go b/vendor/honnef.co/go/tools/go/types/typeutil/typeparams.go index 15b20a74..a53c0955 100644 --- a/vendor/honnef.co/go/tools/go/types/typeutil/typeparams.go +++ b/vendor/honnef.co/go/tools/go/types/typeutil/typeparams.go @@ -49,6 +49,9 @@ func (ts TypeSet) CoreType() types.Type { if !ok { return nil } + if !types.Identical(ch1.Elem(), ch2.Elem()) { + return nil + } if ch1.Dir() == types.SendRecv { // typ is currently a bidirectional channel. The term's type is either also bidirectional, or // unidirectional. Use the term's type. @@ -100,7 +103,12 @@ func Any(typ types.Type, fn func(*types.Term) bool) bool { return NewTypeSet(typ).Any(fn) } -func IsSlice(term *types.Term) bool { - _, ok := term.Type().Underlying().(*types.Slice) +func IsType[T types.Type](term *types.Term) bool { + _, ok := term.Type().Underlying().(T) return ok } + +//go:fix inline +func IsSlice(term *types.Term) bool { + return IsType[*types.Slice](term) +} diff --git a/vendor/honnef.co/go/tools/go/types/typeutil/unify.go b/vendor/honnef.co/go/tools/go/types/typeutil/unify.go new file mode 100644 index 00000000..ced507ca --- /dev/null +++ b/vendor/honnef.co/go/tools/go/types/typeutil/unify.go @@ -0,0 +1,345 @@ +// 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. + +// The code in this file is copied from +// x/tools/gopls/internal/golang/implementation.go + +package typeutil + +import ( + "fmt" + "go/types" + "iter" + "reflect" +) + +// Unify reports whether the types of x and y match. +// +// If unifier is nil, unify reports only whether it succeeded. +// If unifier is non-nil, it is populated with the values +// of type parameters determined during a successful unification. +// If unification succeeds without binding a type parameter, that parameter +// will not be present in the map. +// +// On entry, the unifier's contents are treated as the values of already-bound type +// parameters, constraining the unification. +// +// For example, if unifier is an empty (not nil) map on entry, then the types +// +// func[T any](T, int) +// +// and +// +// func[U any](bool, U) +// +// will unify, with T=bool and U=int. +// That is, the contents of unifier after unify returns will be +// +// {T: bool, U: int} +// +// where "T" is the type parameter T and "bool" is the basic type for bool. +// +// But if unifier is {T: int} is int on entry, then unification will fail, because T +// does not unify with bool. +// +// Unify does not preserve aliases. For example, given the following: +// +// type String = string +// type A[T] = T +// +// unification succeeds with T bound to string, not String. +// +// See also: unify in cache/methodsets/fingerprint, which implements +// unification for type fingerprints, for the global index. +// +// BUG: literal interfaces are not handled properly. But this function is currently +// used only for signatures, where such types are very rare. +func Unify(x, y types.Type, unifier map[*types.TypeParam]types.Type) bool { + // bindings[tp] is the binding for type parameter tp. + // Although type parameters are nominally bound to types, each bindings[tp] + // is a pointer to a type, so unbound variables that unify can share a binding. + bindings := map[*types.TypeParam]*types.Type{} + + // Bindings is initialized with pointers to the provided types. + for tp, t := range unifier { + bindings[tp] = &t + } + + // bindingFor returns the *types.Type in bindings for tp if tp is not nil, + // creating one if needed. + bindingFor := func(tp *types.TypeParam) *types.Type { + if tp == nil { + return nil + } + b := bindings[tp] + if b == nil { + b = new(types.Type) + bindings[tp] = b + } + return b + } + + // bind sets b to t if b does not occur in t. + bind := func(b *types.Type, t types.Type) bool { + for tp := range typeParams(t) { + if b == bindings[tp] { + return false // failed "occurs" check + } + } + *b = t + return true + } + + // uni performs the actual unification. + depth := 0 + var uni func(x, y types.Type) bool + uni = func(x, y types.Type) bool { + // Panic if recursion gets too deep, to detect bugs before + // overflowing the stack. + depth++ + defer func() { depth-- }() + if depth > 100 { + panic("unify: max depth exceeded") + } + + x = types.Unalias(x) + y = types.Unalias(y) + + tpx, _ := x.(*types.TypeParam) + tpy, _ := y.(*types.TypeParam) + if tpx != nil || tpy != nil { + // Identical type params unify. + if tpx == tpy { + return true + } + bx := bindingFor(tpx) + by := bindingFor(tpy) + + // If both args are type params and neither is bound, have them share a binding. + if bx != nil && by != nil && *bx == nil && *by == nil { + // Arbitrarily give y's binding to x. + bindings[tpx] = by + return true + } + // Treat param bindings like original args in what follows. + if bx != nil && *bx != nil { + x = *bx + } + if by != nil && *by != nil { + y = *by + } + // If the x param is unbound, bind it to y. + if bx != nil && *bx == nil { + return bind(bx, y) + } + // If the y param is unbound, bind it to x. + if by != nil && *by == nil { + return bind(by, x) + } + // Unify the binding of a bound parameter. + return uni(x, y) + } + + // Neither arg is a type param. + + if reflect.TypeOf(x) != reflect.TypeOf(y) { + return false // mismatched types + } + + switch x := x.(type) { + case *types.Array: + y := y.(*types.Array) + return x.Len() == y.Len() && + uni(x.Elem(), y.Elem()) + + case *types.Basic: + y := y.(*types.Basic) + return x.Kind() == y.Kind() + + case *types.Chan: + y := y.(*types.Chan) + return x.Dir() == y.Dir() && + uni(x.Elem(), y.Elem()) + + case *types.Interface: + y := y.(*types.Interface) + // TODO(adonovan,jba): fix: for correctness, we must check + // that both interfaces have the same set of methods + // modulo type parameters, while avoiding the risk of + // unbounded interface recursion. + // + // Since non-empty interface literals are vanishingly + // rare in methods signatures, we ignore this for now. + // If more precision is needed we could compare method + // names and arities, still without full recursion. + return x.NumMethods() == y.NumMethods() + + case *types.Map: + y := y.(*types.Map) + return uni(x.Key(), y.Key()) && + uni(x.Elem(), y.Elem()) + + case *types.Named: + y := y.(*types.Named) + if x.Origin() != y.Origin() { + return false // different named types + } + xtargs := x.TypeArgs() + ytargs := y.TypeArgs() + if xtargs.Len() != ytargs.Len() { + return false // arity error (ill-typed) + } + for i := range xtargs.Len() { + if !uni(xtargs.At(i), ytargs.At(i)) { + return false // mismatched type args + } + } + return true + + case *types.Pointer: + y := y.(*types.Pointer) + return uni(x.Elem(), y.Elem()) + + case *types.Signature: + y := y.(*types.Signature) + return x.Variadic() == y.Variadic() && + uni(x.Params(), y.Params()) && + uni(x.Results(), y.Results()) + + case *types.Slice: + y := y.(*types.Slice) + return uni(x.Elem(), y.Elem()) + + case *types.Struct: + y := y.(*types.Struct) + if x.NumFields() != y.NumFields() { + return false + } + for i := range x.NumFields() { + xf := x.Field(i) + yf := y.Field(i) + if xf.Embedded() != yf.Embedded() || + xf.Name() != yf.Name() || + x.Tag(i) != y.Tag(i) || + !xf.Exported() && xf.Pkg() != yf.Pkg() || + !uni(xf.Type(), yf.Type()) { + return false + } + } + return true + + case *types.Tuple: + y := y.(*types.Tuple) + if x.Len() != y.Len() { + return false + } + for i := range x.Len() { + if !uni(x.At(i).Type(), y.At(i).Type()) { + return false + } + } + return true + + default: // incl. *Union, *TypeParam + panic(fmt.Sprintf("unexpected Type %#v", x)) + } + } + + if !uni(x, y) { + clear(unifier) + return false + } + + // Populate the input map with the resulting types. + if unifier != nil { + for tparam, tptr := range bindings { + unifier[tparam] = *tptr + } + } + return true +} + +// typeParams yields all the free type parameters within t that are relevant for +// unification. +// +// Note: this function is tailored for the specific needs of the unification algorithm. +// Don't try to use it for other purposes, see [typeparams.Free] instead. +func typeParams(t types.Type) iter.Seq[*types.TypeParam] { + return func(yield func(*types.TypeParam) bool) { + seen := map[*types.TypeParam]bool{} // yield each type param only once + + // tps(t) yields each TypeParam in t and returns false to stop. + var tps func(types.Type) bool + tps = func(t types.Type) bool { + t = types.Unalias(t) + + switch t := t.(type) { + case *types.TypeParam: + if seen[t] { + return true + } + seen[t] = true + return yield(t) + + case *types.Basic: + return true + + case *types.Array: + return tps(t.Elem()) + + case *types.Chan: + return tps(t.Elem()) + + case *types.Interface: + // TODO(jba): implement. + return true + + case *types.Map: + return tps(t.Key()) && tps(t.Elem()) + + case *types.Named: + if t.Origin() == t { + // generic type: look at type params + return every(t.TypeParams().TypeParams(), + func(tp *types.TypeParam) bool { return tps(tp) }) + } + // instantiated type: look at type args + return every(t.TypeArgs().Types(), tps) + + case *types.Pointer: + return tps(t.Elem()) + + case *types.Signature: + return tps(t.Params()) && tps(t.Results()) + + case *types.Slice: + return tps(t.Elem()) + + case *types.Struct: + return every(t.Fields(), + func(v *types.Var) bool { return tps(v.Type()) }) + + case *types.Tuple: + return every(t.Variables(), + func(v *types.Var) bool { return tps(v.Type()) }) + + default: // incl. *Union + panic(fmt.Sprintf("unexpected Type %#v", t)) + } + } + + tps(t) + } +} + +// every reports whether every pred(t) for t in seq returns true, +// stopping at the first false element. +func every[T any](seq iter.Seq[T], pred func(T) bool) bool { + for t := range seq { + if !pred(t) { + return false + } + } + return true +} diff --git a/vendor/honnef.co/go/tools/internal/iterutil/iterutil.go b/vendor/honnef.co/go/tools/internal/iterutil/iterutil.go new file mode 100644 index 00000000..e2f1f093 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/iterutil/iterutil.go @@ -0,0 +1,21 @@ +package iterutil + +import "iter" + +func All[T any](seq iter.Seq[T], fn func(T) bool) bool { + for v := range seq { + if !fn(v) { + return false + } + } + return true +} + +func Any[T any](seq iter.Seq[T], fn func(T) bool) bool { + for v := range seq { + if fn(v) { + return true + } + } + return false +} diff --git a/vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go b/vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go index 5db18e30..1e2cc493 100644 --- a/vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go +++ b/vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go @@ -11,7 +11,6 @@ package buildir import ( - "go/types" "reflect" "honnef.co/go/tools/go/ir" @@ -63,20 +62,10 @@ func run(pass *analysis.Pass) (any, error) { prog.SetNoReturn(cfgs.NoReturn) - // Create IR packages for all imports. - // Order is not significant. - created := make(map[*types.Package]bool) - var createAll func(pkgs []*types.Package) - createAll = func(pkgs []*types.Package) { - for _, p := range pkgs { - if !created[p] { - created[p] = true - prog.CreatePackage(p, nil, nil, true) - createAll(p.Imports()) - } - } + // Create IR packages for direct imports. + for _, p := range pass.Pkg.Imports() { + prog.CreatePackage(p, nil, nil, true) } - createAll(pass.Pkg.Imports()) // Create and build the primary package. irpkg := prog.CreatePackage(pass.Pkg, pass.Files, pass.TypesInfo, false) diff --git a/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go b/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go index f07496dd..6bae3f78 100644 --- a/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go +++ b/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go @@ -13,7 +13,6 @@ import ( "honnef.co/go/tools/analysis/report" "honnef.co/go/tools/go/ast/astutil" "honnef.co/go/tools/go/ir" - "honnef.co/go/tools/go/ir/irutil" "honnef.co/go/tools/go/types/typeutil" "honnef.co/go/tools/internal/passes/buildir" @@ -58,7 +57,7 @@ func CheckRangeStringRunes(pass *analysis.Pass) (any, error) { // Expect two refs: one for obtaining the length of the slice, // one for accessing the elements - if len(irutil.FilterDebug(*refs)) != 2 { + if len(*refs) != 2 { // TODO(dh): right now, we check that only one place // refers to our slice. This will miss cases such as // ranging over the slice twice. Ideally, we'd ensure that diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/aliases/aliases.go b/vendor/honnef.co/go/tools/internal/xtools-internal/aliases/aliases.go new file mode 100644 index 00000000..a4ae04bc --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/aliases/aliases.go @@ -0,0 +1,18 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package aliases + +import ( + "go/token" + "go/types" +) + +// New creates a new TypeName in Package pkg that +// is an alias for the type rhs. +func New(pos token.Pos, pkg *types.Package, name string, rhs types.Type, tparams []*types.TypeParam) *types.TypeName { + tname := types.NewTypeName(pos, pkg, name, nil) + types.NewAlias(tname, rhs).SetTypeParams(tparams) + return tname +} diff --git a/vendor/honnef.co/go/tools/internal/analysisinternal/typeindex/typeindex.go b/vendor/honnef.co/go/tools/internal/xtools-internal/analysis/typeindex/typeindex.go similarity index 80% rename from vendor/honnef.co/go/tools/internal/analysisinternal/typeindex/typeindex.go rename to vendor/honnef.co/go/tools/internal/xtools-internal/analysis/typeindex/typeindex.go index 44d207c0..1bd7b78e 100644 --- a/vendor/honnef.co/go/tools/internal/analysisinternal/typeindex/typeindex.go +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/analysis/typeindex/typeindex.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package typeindex defines an analyzer that provides a -// [golang.org/x/tools/internal/typesinternal/typeindex.Index]. +// [honnef.co/go/tools/internal/xtools-internal/typesinternal/typeindex.Index]. // // Like [golang.org/x/tools/go/analysis/passes/inspect], it is // intended to be used as a helper by other analyzers; it reports no @@ -16,13 +16,13 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/ast/inspector" - "honnef.co/go/tools/internal/typesinternal/typeindex" + "honnef.co/go/tools/internal/xtools-internal/typesinternal/typeindex" ) var Analyzer = &analysis.Analyzer{ Name: "typeindex", Doc: "indexes of type information for later passes", - URL: "https://pkg.go.dev/golang.org/x/tools/internal/analysisinternal/typeindex", + URL: "https://pkg.go.dev/honnef.co/go/tools/internal/xtools-internal/analysis/typeindex", Run: func(pass *analysis.Pass) (any, error) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) return typeindex.New(inspect, pass.Pkg, pass.TypesInfo), nil diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/clone.go b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/clone.go new file mode 100644 index 00000000..2c9b6bb4 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/clone.go @@ -0,0 +1,71 @@ +// Copyright 2023 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 astutil + +import ( + "go/ast" + "reflect" +) + +// CloneNode returns a deep copy of a Node. +// It omits pointers to ast.{Scope,Object} variables. +func CloneNode[T ast.Node](n T) T { + return cloneNode(n).(T) +} + +func cloneNode(n ast.Node) ast.Node { + var clone func(x reflect.Value) reflect.Value + set := func(dst, src reflect.Value) { + src = clone(src) + if src.IsValid() { + dst.Set(src) + } + } + clone = func(x reflect.Value) reflect.Value { + switch x.Kind() { + case reflect.Pointer: + if x.IsNil() { + return x + } + // Skip fields of types potentially involved in cycles. + switch x.Interface().(type) { + case *ast.Object, *ast.Scope: + return reflect.Zero(x.Type()) + } + y := reflect.New(x.Type().Elem()) + set(y.Elem(), x.Elem()) + return y + + case reflect.Struct: + y := reflect.New(x.Type()).Elem() + for i := 0; i < x.Type().NumField(); i++ { + set(y.Field(i), x.Field(i)) + } + return y + + case reflect.Slice: + if x.IsNil() { + return x + } + y := reflect.MakeSlice(x.Type(), x.Len(), x.Cap()) + for i := 0; i < x.Len(); i++ { + set(y.Index(i), x.Index(i)) + } + return y + + case reflect.Interface: + y := reflect.New(x.Type()).Elem() + set(y, x.Elem()) + return y + + case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer: + panic(x) // unreachable in AST + + default: + return x // bool, string, number + } + } + return clone(reflect.ValueOf(n)).Interface().(ast.Node) +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/comment.go b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/comment.go new file mode 100644 index 00000000..40a34721 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/comment.go @@ -0,0 +1,143 @@ +// Copyright 2025 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 astutil + +import ( + "go/ast" + "go/token" + "iter" + "sort" + "strings" +) + +// Deprecation returns the paragraph of the doc comment that starts with the +// conventional "Deprecation: " marker, as defined by +// https://go.dev/wiki/Deprecated, or "" if the documented symbol is not +// deprecated. +func Deprecation(doc *ast.CommentGroup) string { + for p := range strings.SplitSeq(doc.Text(), "\n\n") { + // There is still some ambiguity for deprecation message. This function + // only returns the paragraph introduced by "Deprecated: ". More + // information related to the deprecation may follow in additional + // paragraphs, but the deprecation message should be able to stand on + // its own. See golang/go#38743. + if strings.HasPrefix(p, "Deprecated: ") { + return p + } + } + return "" +} + +// -- plundered from the future (CL 605517, issue #68021) -- + +// TODO(adonovan): replace with ast.Directive in go1.26 (#68021). +// Beware of our local mods to handle analysistest +// "want" comments on the same line. + +// A directive is a comment line with special meaning to the Go +// toolchain or another tool. It has the form: +// +// //tool:name args +// +// The "tool:" portion is missing for the three directives named +// line, extern, and export. +// +// See https://go.dev/doc/comment#Syntax for details of Go comment +// syntax and https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives +// for details of directives used by the Go compiler. +type Directive struct { + Pos token.Pos // of preceding "//" + Tool string + Name string + Args string // may contain internal spaces +} + +// isDirective reports whether c is a comment directive. +// This code is also in go/printer. +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 +} + +// Directives returns the directives within the comment. +func Directives(g *ast.CommentGroup) (res []*Directive) { + if g != nil { + // Avoid (*ast.CommentGroup).Text() as it swallows directives. + for _, c := range g.List { + if len(c.Text) > 2 && + c.Text[1] == '/' && + c.Text[2] != ' ' && + isDirective(c.Text[2:]) { + + tool, nameargs, ok := strings.Cut(c.Text[2:], ":") + if !ok { + // Must be one of {line,extern,export}. + tool, nameargs = "", tool + } + name, args, _ := strings.Cut(nameargs, " ") // tab?? + // Permit an additional line comment after the args, chiefly to support + // [golang.org/x/tools/go/analysis/analysistest]. + args, _, _ = strings.Cut(args, "//") + res = append(res, &Directive{ + Pos: c.Slash, + Tool: tool, + Name: name, + Args: strings.TrimSpace(args), + }) + } + } + } + return +} + +// Comments returns an iterator over the comments overlapping the specified interval. +// Comments are sorted by position in the file, so we can use binary search. +func Comments(file *ast.File, start, end token.Pos) iter.Seq[*ast.Comment] { + return func(yield func(*ast.Comment) bool) { + // Find the first comment group that overlaps the range. + i := sort.Search(len(file.Comments), func(i int) bool { + return file.Comments[i].End() >= start + }) + for _, cg := range file.Comments[i:] { + if cg.Pos() > end { + return + } + // Find the first comment in the group that overlaps the range. + j := sort.Search(len(cg.List), func(j int) bool { + return cg.List[j].End() >= start + }) + for _, co := range cg.List[j:] { + if co.Pos() > end { + return + } + if !yield(co) { + return + } + } + } + } +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/cursor.go b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/cursor.go new file mode 100644 index 00000000..74e75d14 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/cursor.go @@ -0,0 +1,38 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package astutil + +import ( + "go/ast" + + "golang.org/x/tools/go/ast/edge" + "golang.org/x/tools/go/ast/inspector" +) + +// UnparenCursor returns the cursor for an expression with any +// enclosing parentheses removed, similar to [ast.Unparen]. +// It is often prudent to call this before switching on the +// type of cur.Node(). +// +// See also [UnparenEnclosingCursor]. +func UnparenCursor(cur inspector.Cursor) inspector.Cursor { + for is[*ast.ParenExpr](cur) { + cur, _ = cur.FirstChild() + } + return cur +} + +// UnparenEnclosingCursor returns the first element of +// the [Cursor.Enclosing] sequence that is not itself enclosed +// in parens. It is often prudent to call this before switching on +// cur.ParentEdge(). +// +// See also [UnparenCursor]. +func UnparenEnclosingCursor(cur inspector.Cursor) inspector.Cursor { + for cur.ParentEdgeKind() == edge.ParenExpr_X { + cur = cur.Parent() + } + return cur +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/equal.go b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/equal.go new file mode 100644 index 00000000..210f3923 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/equal.go @@ -0,0 +1,107 @@ +// Copyright 2023 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 astutil + +import ( + "go/ast" + "go/token" + "reflect" +) + +// Equal reports whether two nodes are structurally equal, +// ignoring fields of type [token.Pos], [ast.Object], +// and [ast.Scope], and comments. +// +// The operands x and y may be nil. +// A nil slice is not equal to an empty slice. +// +// The provided function determines whether two identifiers +// should be considered identical. +func Equal(x, y ast.Node, identical func(x, y *ast.Ident) bool) bool { + if x == nil || y == nil { + return x == y + } + return equal(reflect.ValueOf(x), reflect.ValueOf(y), identical) +} + +// EqualSyntax reports whether x and y are equal. +// Identifiers are considered equal if they are spelled the same. +// Comments are ignored. +func EqualSyntax(x, y ast.Expr) bool { + sameName := func(x, y *ast.Ident) bool { return x.Name == y.Name } + return Equal(x, y, sameName) +} + +func equal(x, y reflect.Value, identical func(x, y *ast.Ident) bool) bool { + // Ensure types are the same + if x.Type() != y.Type() { + return false + } + switch x.Kind() { + case reflect.Pointer: + if x.IsNil() || y.IsNil() { + return x.IsNil() == y.IsNil() + } + switch t := x.Interface().(type) { + // Skip fields of types potentially involved in cycles. + case *ast.Object, *ast.Scope, *ast.CommentGroup: + return true + case *ast.Ident: + return identical(t, y.Interface().(*ast.Ident)) + default: + return equal(x.Elem(), y.Elem(), identical) + } + + case reflect.Interface: + if x.IsNil() || y.IsNil() { + return x.IsNil() == y.IsNil() + } + return equal(x.Elem(), y.Elem(), identical) + + case reflect.Struct: + for i := range x.NumField() { + xf := x.Field(i) + yf := y.Field(i) + // Skip position fields. + if xpos, ok := xf.Interface().(token.Pos); ok { + ypos := yf.Interface().(token.Pos) + // Numeric value of a Pos is not significant but its "zeroness" is, + // because it is often significant, e.g. CallExpr.Variadic(Ellipsis), ChanType.Arrow. + if xpos.IsValid() != ypos.IsValid() { + return false + } + } else if !equal(xf, yf, identical) { + return false + } + } + return true + + case reflect.Slice: + if x.IsNil() || y.IsNil() { + return x.IsNil() == y.IsNil() + } + if x.Len() != y.Len() { + return false + } + for i := range x.Len() { + if !equal(x.Index(i), y.Index(i), identical) { + return false + } + } + return true + + case reflect.String: + return x.String() == y.String() + + case reflect.Bool: + return x.Bool() == y.Bool() + + case reflect.Int: + return x.Int() == y.Int() + + default: + panic(x) + } +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/fields.go b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/fields.go new file mode 100644 index 00000000..8b81ea47 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/fields.go @@ -0,0 +1,35 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package astutil + +import ( + "go/ast" + "iter" +) + +// FlatFields 'flattens' an ast.FieldList, returning an iterator over each +// (name, field) combination in the list. For unnamed fields, the identifier is +// nil. +func FlatFields(list *ast.FieldList) iter.Seq2[*ast.Ident, *ast.Field] { + return func(yield func(*ast.Ident, *ast.Field) bool) { + if list == nil { + return + } + + for _, field := range list.List { + if len(field.Names) == 0 { + if !yield(nil, field) { + return + } + } else { + for _, name := range field.Names { + if !yield(name, field) { + return + } + } + } + } + } +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/purge.go b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/purge.go new file mode 100644 index 00000000..d0a39415 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/purge.go @@ -0,0 +1,93 @@ +// Copyright 2023 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 astutil provides various AST utility functions for gopls. +package astutil + +import ( + "bytes" + "go/scanner" + "go/token" +) + +// PurgeFuncBodies returns a copy of src in which the contents of each +// outermost {...} region have been deleted, except for struct and +// interface type bodies and the bodies of length-elided array +// literals ([...]T), whose element count is part of the type. It +// includes function bodies, function-literal bodies, and the bodies +// of slice, map, and explicitly-sized array composite literals (whose +// contents don't affect the type of the enclosing declaration). This +// reduces the amount of work required to parse the top-level +// declarations. +// +// PurgeFuncBodies does not preserve newlines or position information. +// Also, if the input is invalid, parsing the output of +// PurgeFuncBodies may result in a different tree due to its effects +// on parser error recovery. +func PurgeFuncBodies(src []byte) []byte { + // Destroy the content of any {...}-bracketed regions that are + // not immediately preceded by a "struct" or "interface" token, + // and that are not the body of a length-elided array literal. + // That includes function bodies, switch/select bodies, and most + // composite literals; this will lead to non-void functions that + // don't have return statements, which of course is a type error, + // but that's ok. + + var out bytes.Buffer + file := token.NewFileSet().AddFile("", -1, len(src)) + var sc scanner.Scanner + sc.Init(file, src, nil, 0) + var prev token.Token + var cursor int // last consumed src offset + var braces []token.Pos // stack of unclosed braces, or -1 for a region we preserve + var ellipsis bool // saw "[...]" not yet consumed by a literal-body "{" + for { + pos, tok, _ := sc.Scan() + if tok == token.EOF { + break + } + switch tok { + case token.COMMENT: + // TODO(adonovan): opt: skip, to save an estimated 20% of time. + + case token.SEMICOLON: + ellipsis = false + + case token.RBRACK: + // "...]" occurs only in the array-type prefix of a + // composite literal; variadic "..." is followed by + // a type or ")", never "]". + if prev == token.ELLIPSIS { + ellipsis = true + } + + case token.LBRACE: + if prev == token.STRUCT || prev == token.INTERFACE { + pos = -1 // type body: preserve (don't consume ellipsis) + } else if ellipsis { + pos = -1 // [...]T literal body: preserve + ellipsis = false + } + braces = append(braces, pos) + + case token.RBRACE: + if last := len(braces) - 1; last >= 0 { + top := braces[last] + braces = braces[:last] + if top < 0 { + // preserve + } else if len(braces) == 0 { // toplevel only + // Delete {...} body. + start := file.Offset(top) + end := file.Offset(pos) + out.Write(src[cursor : start+len("{")]) + cursor = end + } + } + } + prev = tok + } + out.Write(src[cursor:]) + return out.Bytes() +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/stringlit.go b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/stringlit.go new file mode 100644 index 00000000..eb49d451 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/stringlit.go @@ -0,0 +1,103 @@ +// Copyright 2025 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 astutil + +import ( + "fmt" + "go/ast" + "go/token" + "strconv" + "unicode/utf8" +) + +// RangeInStringLiteral calculates the positional range within a string literal +// corresponding to the specified start and end byte offsets within the logical string. +func RangeInStringLiteral(lit *ast.BasicLit, start, end int) (Range, error) { + startPos, err := PosInStringLiteral(lit, start) + if err != nil { + return Range{}, fmt.Errorf("start: %v", err) + } + endPos, err := PosInStringLiteral(lit, end) + if err != nil { + return Range{}, fmt.Errorf("end: %v", err) + } + return Range{startPos, endPos}, nil +} + +// PosInStringLiteral returns the position within a string literal +// corresponding to the specified byte offset within the logical +// string that it denotes. +func PosInStringLiteral(lit *ast.BasicLit, offset int) (token.Pos, error) { + raw := lit.Value + + value, err := strconv.Unquote(raw) + if err != nil { + return 0, err + } + if !(0 <= offset && offset <= len(value)) { + return 0, fmt.Errorf("invalid offset") + } + + pos, _ := walkStringLiteral(lit, lit.End(), offset) + return pos, nil +} + +// OffsetInStringLiteral returns the byte offset within the logical (unquoted) +// string corresponding to the specified source position. +func OffsetInStringLiteral(lit *ast.BasicLit, pos token.Pos) (int, error) { + if !NodeContainsPos(lit, pos) { + return 0, fmt.Errorf("invalid position") + } + + raw := lit.Value + + value, err := strconv.Unquote(raw) + if err != nil { + return 0, err + } + + _, offset := walkStringLiteral(lit, pos, len(value)) + return offset, nil +} + +// walkStringLiteral iterates through the raw string literal to map between +// a file position and a logical byte offset. It stops when it reaches +// either the targetPos or the targetOffset. +// +// TODO(hxjiang): consider making an iterator. +func walkStringLiteral(lit *ast.BasicLit, targetPos token.Pos, targetOffset int) (token.Pos, int) { + raw := lit.Value + norm := int(lit.End()-lit.Pos()) > len(lit.Value) + + // remove quotes + quote := raw[0] // '"' or '`' + raw = raw[1 : len(raw)-1] + + var ( + i = 0 // byte index within logical value + pos = lit.Pos() + 1 // position within literal + ) + + for raw != "" { + r, _, rest, _ := strconv.UnquoteChar(raw, quote) // can't fail + sz := len(raw) - len(rest) // length of literal char in raw bytes + + nextPos := pos + token.Pos(sz) + if norm && r == '\n' { + nextPos++ + } + nextI := i + utf8.RuneLen(r) // length of logical char in "cooked" bytes + + if nextPos > targetPos || nextI > targetOffset { + break + } + + raw = raw[sz:] + i = nextI + pos = nextPos + } + + return pos, i +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/unpack.go b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/unpack.go new file mode 100644 index 00000000..3d2fb04f --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/unpack.go @@ -0,0 +1,61 @@ +// Copyright 2023 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 astutil + +import ( + "go/ast" + + "honnef.co/go/tools/internal/xtools-internal/typeparams" +) + +// UnpackRecv unpacks a receiver type expression, reporting whether it is a +// pointer receiver, along with the type name identifier and any receiver type +// parameter identifiers. +// +// Copied (with modifications) from go/types. +func UnpackRecv(rtyp ast.Expr) (ptr bool, rname *ast.Ident, tparams []*ast.Ident) { +L: // unpack receiver type + // This accepts invalid receivers such as ***T and does not + // work for other invalid receivers, but we don't care. The + // validity of receiver expressions is checked elsewhere. + for { + switch t := rtyp.(type) { + case *ast.ParenExpr: + rtyp = t.X + case *ast.StarExpr: + ptr = true + rtyp = t.X + default: + break L + } + } + + // unpack type parameters, if any + switch rtyp.(type) { + case *ast.IndexExpr, *ast.IndexListExpr: + var indices []ast.Expr + rtyp, _, indices, _ = typeparams.UnpackIndexExpr(rtyp) + for _, arg := range indices { + var par *ast.Ident + switch arg := arg.(type) { + case *ast.Ident: + par = arg + default: + // ignore errors + } + if par == nil { + par = &ast.Ident{NamePos: arg.Pos(), Name: "_"} + } + tparams = append(tparams, par) + } + } + + // unpack receiver name + if name, _ := rtyp.(*ast.Ident); name != nil { + rname = name + } + + return +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/util.go b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/util.go new file mode 100644 index 00000000..6d974db6 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/astutil/util.go @@ -0,0 +1,261 @@ +// Copyright 2025 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 astutil + +import ( + "fmt" + "go/ast" + "go/printer" + "go/token" + "strings" + + "golang.org/x/tools/go/ast/inspector" + "honnef.co/go/tools/internal/xtools-internal/moreiters" +) + +// NodeContains reports whether the Pos/End range of node n encloses +// the given range. +// +// It is inclusive of both end points, to allow hovering (etc) when +// the cursor is immediately after a node. +// +// Like [NodeRange], it treats the range of an [ast.File] as the +// file's complete extent. +// +// Precondition: n must not be nil. +func NodeContains(n ast.Node, rng Range) bool { + return NodeRange(n).Contains(rng) +} + +// NodeContainsPos reports whether the Pos/End range of node n encloses +// the given pos. +// +// Like [NodeRange], it treats the range of an [ast.File] as the +// file's complete extent. +func NodeContainsPos(n ast.Node, pos token.Pos) bool { + return NodeRange(n).ContainsPos(pos) +} + +// EnclosingFile returns the syntax tree for the file enclosing c. +// +// TODO(adonovan): promote this to a method of Cursor. +func EnclosingFile(c inspector.Cursor) *ast.File { + c, _ = moreiters.First(c.Enclosing((*ast.File)(nil))) + return c.Node().(*ast.File) +} + +// DocComment returns the doc comment for a node, if any. +func DocComment(n ast.Node) *ast.CommentGroup { + switch n := n.(type) { + case *ast.FuncDecl: + return n.Doc + case *ast.GenDecl: + return n.Doc + case *ast.ValueSpec: + return n.Doc + case *ast.TypeSpec: + return n.Doc + case *ast.File: + return n.Doc + case *ast.ImportSpec: + return n.Doc + case *ast.Field: + return n.Doc + } + return nil +} + +// Format returns a string representation of the node n. +func Format(fset *token.FileSet, n ast.Node) string { + var buf strings.Builder + printer.Fprint(&buf, fset, n) // ignore errors + return buf.String() +} + +// -- Range -- + +// Range is a Pos interval. +// It implements [analysis.Range] and [ast.Node]. +type Range struct{ Start, EndPos token.Pos } + +// RangeOf constructs a Range. +// +// RangeOf exists to pacify the "unkeyed literal" (composites) vet +// check. It would be nice if there were a way for a type to add +// itself to the allowlist. +func RangeOf(start, end token.Pos) Range { return Range{start, end} } + +// NodeRange returns the extent of node n as a Range. +// +// For unfortunate historical reasons, the Pos/End extent of an +// ast.File runs from the start of its package declaration---excluding +// copyright comments, build tags, and package documentation---to the +// end of its last declaration, excluding any trailing comments. So, +// as a special case, if n is an [ast.File], NodeContains uses +// n.FileStart <= pos && pos <= n.FileEnd to report whether the +// position lies anywhere within the file. +func NodeRange(n ast.Node) Range { + if file, ok := n.(*ast.File); ok { + return Range{file.FileStart, file.FileEnd} // entire file + } + return Range{n.Pos(), n.End()} +} + +func (r Range) Pos() token.Pos { return r.Start } +func (r Range) End() token.Pos { return r.EndPos } + +// ContainsPos reports whether the range (inclusive of both end points) +// includes the specified position. +func (r Range) ContainsPos(pos token.Pos) bool { + return r.Contains(RangeOf(pos, pos)) +} + +// Contains reports whether the range (inclusive of both end points) +// includes the specified range. +func (r Range) Contains(rng Range) bool { + return r.Start <= rng.Start && rng.EndPos <= r.EndPos +} + +// IsValid reports whether the range is valid. +func (r Range) IsValid() bool { return r.Start.IsValid() && r.Start <= r.EndPos } + +// -- + +// Select returns the syntax nodes identified by a user's text +// selection. It returns three nodes: the innermost node that wholly +// encloses the selection; and the first and last nodes that are +// wholly enclosed by the selection. +// +// For example, given this selection: +// +// { f(); g(); /* comment */ } +// ~~~~~~~~~~~ +// +// Select returns the enclosing BlockStmt, the f() CallExpr, and the g() CallExpr. +// +// If the selection does not wholly enclose any nodes, Select returns an error +// and invalid start/end nodes, but it may return a valid enclosing node. +// +// Callers that require exactly one syntax tree (e.g. just f() or just +// g()) should check that the returned start and end nodes are +// identical. +// +// This function is intended to be called early in the handling of a +// user's request, since it is tolerant of sloppy selection including +// extraneous whitespace and comments. Use it in new code instead of +// PathEnclosingInterval. When the exact extent of a node is known, +// use [Cursor.FindByPos] instead. +// +// TODO(hxjiang): Consider refactoring the function signature. It is currently +// confusing that an error is returned even when a valid enclosing node is +// successfully found. Consider grouping all cursors into one struct. +func Select(curFile inspector.Cursor, start, end token.Pos) (_enclosing, _start, _end inspector.Cursor, _ error) { + curEnclosing, ok := curFile.FindByPos(start, end) + if !ok { + return noCursor, noCursor, noCursor, fmt.Errorf("invalid selection") + } + + // Find the first and last node wholly within the (start, end) range. + // We'll narrow the effective selection to them, to exclude whitespace. + // (This matches the functionality of PathEnclosingInterval.) + var curStart, curEnd inspector.Cursor + rng := RangeOf(start, end) + for cur := range curEnclosing.Preorder() { + if rng.Contains(NodeRange(cur.Node())) { + // The start node has the least Pos. + if !curStart.Valid() { + curStart = cur + } + // The end node has the greatest End. + // End positions do not change monotonically, + // so we must compute the max. + if !curEnd.Valid() || + cur.Node().End() > curEnd.Node().End() { + curEnd = cur + } + } + } + if !curStart.Valid() { + // The selection is valid (inside curEnclosing) but contains no + // complete nodes. This happens for point selections (start == end), + // or selections covering only only spaces, comments, and punctuation + // tokens. + // Return the enclosing node so the caller can still use the context. + return curEnclosing, noCursor, noCursor, fmt.Errorf("invalid selection") + } + return curEnclosing, curStart, curEnd, nil +} + +var noCursor inspector.Cursor + +// MaybeParenthesize returns new, possibly wrapped in parens if needed +// to preserve operator precedence when it replaces old, whose parent +// is parentNode. +// +// (This would be more naturally written in terms of Cursor, but one of +// the callers--the inliner--does not have cursors handy.) +func MaybeParenthesize(parentNode ast.Node, old, new ast.Expr) ast.Expr { + if needsParens(parentNode, old, new) { + new = &ast.ParenExpr{X: new} + } + return new +} + +func needsParens(parentNode ast.Node, old, new ast.Expr) bool { + // An expression beneath a non-expression + // has no precedence ambiguity. + parent, ok := parentNode.(ast.Expr) + if !ok { + return false + } + + precedence := func(n ast.Node) int { + switch n := n.(type) { + case *ast.UnaryExpr, *ast.StarExpr: + return token.UnaryPrec + case *ast.BinaryExpr: + return n.Op.Precedence() + } + return -1 + } + + // Parens are not required if the new node + // is not unary or binary. + newprec := precedence(new) + if newprec < 0 { + return false + } + + // Parens are required if parent and child are both + // unary or binary and the parent has higher precedence. + if precedence(parent) > newprec { + return true + } + + // Was the old node the operand of a postfix operator? + // f().sel + // f()[i:j] + // f()[i] + // f().(T) + // f()(x) + switch parent := parent.(type) { + case *ast.SelectorExpr: + return parent.X == old + case *ast.IndexExpr: + return parent.X == old + case *ast.SliceExpr: + return parent.X == old + case *ast.TypeAssertExpr: + return parent.X == old + case *ast.CallExpr: + return parent.Fun == old + } + return false +} + +func is[T any](n any) bool { + _, ok := n.(T) + return ok +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/graph/allpaths.go b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/allpaths.go new file mode 100644 index 00000000..44732557 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/allpaths.go @@ -0,0 +1,24 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package graph + +// AllPaths returns the set of nodes that are part of at least one path from src to dst. +func AllPaths[NodeID comparable](g Graph[NodeID], src, dst NodeID) map[NodeID]bool { + // We intersect the forward closure of 'src' with + // the reverse closure of 'dst'. This is not the most + // efficient implementation, but it's the clearest, + // and the previous one had bugs. + + fwd := Reachable(g, src) + rev := Reachable(Transpose(g), dst) + + // Intersection + for n := range fwd { + if !rev[n] { + delete(fwd, n) + } + } + return fwd +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/graph/compact.go b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/compact.go new file mode 100644 index 00000000..ae327067 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/compact.go @@ -0,0 +1,89 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package graph + +import "iter" + +// A CompactGraph is a Graph with nodes that are compactly numbered from [0, +// NumNodes()). +// +// Compactly numbered graphs are useful for many graph algorithms, and many +// graph representations are naturally compact. +// +// To compact an arbitrary graph, use [Compact]. +type CompactGraph interface { + Graph[int] + IsCompact() +} + +// A nodePreserving graph is a transformation of another graph that preserves +// node IDs. +type nodePreserving interface { + Graph[int] + unwrapPreservingNodes() Graph[int] +} + +// Compact takes a Graph with arbitrary NodeIDs and returns a compact graph. +// +// If g implements [CompactGraph], it assumes g is already compact and simply +// returns g and an identity mapping. +func Compact[NodeID comparable](g Graph[NodeID]) (CompactGraph, *Index[NodeID]) { + // If it's already compact, simply return it. + if gc, ok := g.(CompactGraph); ok { + // The above assertion ensures NodeID is int, so we know this type + // assertion will always succeed. + return gc, any(NewIdentityIndex(gc.NumNodes())).(*Index[NodeID]) + } + + // If it's a transformation and the underlying graph is compact, we can use + // an identity index. Though we still need to build a compactGraph to + // satisfy the CompactGraph interface. + g2, _ := g.(nodePreserving) + for g2 != nil { + unwrapped := g2.unwrapPreservingNodes() + if gc, ok := unwrapped.(CompactGraph); ok { + index := any(NewIdentityIndex(gc.NumNodes())).(*Index[NodeID]) + cg := compactGraph[NodeID]{g, index} + return &cg, index + } + g2, _ = unwrapped.(nodePreserving) + } + + // Nope, just build an index. + cg := compactGraph[NodeID]{g, NewIndex(g.Nodes())} + return &cg, cg.m +} + +type compactGraph[NodeID comparable] struct { + g Graph[NodeID] + m *Index[NodeID] +} + +func (g *compactGraph[NodeID]) Nodes() iter.Seq[int] { + return func(yield func(int) bool) { + for i := range g.g.NumNodes() { + if !yield(i) { + break + } + } + } +} + +func (g *compactGraph[NodeID]) NumNodes() int { + return g.g.NumNodes() +} + +func (g *compactGraph[NodeID]) Out(node int) iter.Seq[int] { + id := g.m.Value(node) + return func(yield func(int) bool) { + for nid := range g.g.Out(id) { + if !yield(g.m.Index(nid)) { + break + } + } + } +} + +func (g *compactGraph[NodeID]) IsCompact() {} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/graph/graph.go b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/graph.go new file mode 100644 index 00000000..fdea880e --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/graph.go @@ -0,0 +1,33 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package graph provides a common abstraction for directed graphs and standard +// graph algorithms. +// +// In general, this package does not provide or assume any concrete graph +// representation. It's up to the caller of this package to implement the +// [Graph] interface, either directly or as an adapter around another type. +package graph + +import "iter" + +// A Graph implements a directed graph where nodes in the graph are identified +// by the NodeID type. +// +// If a concrete graph type stores additional information about nodes and/or +// edges, it will conventionally provide methods of the form: +// +// Node(node NodeID) nodeInfo +// Edge(from, to NodeID) edgeInfo +type Graph[NodeID comparable] interface { + // Nodes yields all nodes in this graph. + Nodes() iter.Seq[NodeID] + + // NumNodes returns the total number of nodes in this graph. + NumNodes() int + + // Out yields the out-edges of node. Out must be deterministic, though + // otherwise there is no constraint on the order of the returned sequence. + Out(node NodeID) iter.Seq[NodeID] +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/graph/index.go b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/index.go new file mode 100644 index 00000000..394213e4 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/index.go @@ -0,0 +1,96 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package graph + +import ( + "fmt" + "iter" + "slices" +) + +// An Index is an immutable, bijective map between [0, N) and an ordered list of keys. +type Index[Key comparable] struct { + // There are three Index representations: + // + // - If identN > 0, an identity map of [0, identN). + // - If index == nil, a sorted integer index in values. + // - Otherwise, a full index in values and index. + + identN int + values []Key + index map[Key]int +} + +// NewIndex returns an index for the specified list of values. +func NewIndex[Key comparable](values iter.Seq[Key]) *Index[Key] { + vs := slices.Collect(values) + if len(vs) == 0 { + return new(Index[Key]) + } + + // Fast path: a naturally sorted list needs no index. (Sadly, there's no way + // to ask "is Key ordered?") + if vi, ok := any(vs).([]int); ok && slices.IsSorted(vi) { + return &Index[Key]{values: vs} + } + + index := make(map[Key]int, len(vs)) + for i, v := range vs { + index[v] = i + } + return &Index[Key]{values: vs, index: index} +} + +// NewIdentityIndex returns an index that maps [0, n) to [0, n). +func NewIdentityIndex(n int) *Index[int] { + if n < 0 { + panic("n < 0") + } + // If n == 0, this is actually a "sorted integer index", but it doesn't + // matter because everything is out of bounds either way. + return &Index[int]{identN: n} +} + +// Value maps an index to a key. +func (ix *Index[Key]) Value(index int) Key { + if ix.identN > 0 { + if index < 0 || index >= ix.identN { + panic(fmt.Sprintf("index %d out of range [0, %d)", index, ix.identN)) + } + return any(index).(Key) + } + if index < 0 || index >= len(ix.values) { + panic(fmt.Sprintf("index %d out of range [0, %d)", index, ix.identN)) + } + return ix.values[index] +} + +// Index maps a key to an index. +func (ix *Index[Key]) Index(key Key) int { + if key, ok := any(key).(int); ok { + // Integer-only optimizations. + switch { + case ix.identN > 0: + // Identity. + if 0 <= key && key < ix.identN { + return key + } + goto oob + + case ix.index == nil: + // Sorted integers. + if i, ok := slices.BinarySearch(any(ix.values).([]int), key); ok { + return i + } + goto oob + } + } + if i, ok := ix.index[key]; ok { + return i + } + +oob: + panic(fmt.Sprintf("key %v not in index", key)) +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/graph/order.go b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/order.go new file mode 100644 index 00000000..795a434d --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/order.go @@ -0,0 +1,94 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package graph + +import "slices" + +// Postorder returns the sequence of nodes in the spanning DAG of g, in +// postorder. +// +// For rootless subgraphs, it breaks cycles by starting at the lowest numbered +// node. +// +// This algorithm runs in O(V + E) time and O(V + E) space. +func Postorder[NodeID comparable](g Graph[NodeID]) []NodeID { + cg, nodeMap := Compact(g) + + numNodes := cg.NumNodes() + if numNodes == 0 { + return nil + } + + result := make([]NodeID, 0, numNodes) + visited := newBitset(numNodes) + onStack := newBitset(numNodes) + + // visit performs a Depth-First Search. + var visit func(u int) + visit = func(u int) { + if !visited.add(u) { + return + } + onStack.add(u) + + for v := range cg.Out(u) { + if onStack.contains(v) { + // Cycle detected (back-edge). + // To resolve, we simply skip processing this edge further in the + // current recursion, effectively "breaking" the cycle at this point. + continue + } + visit(v) + } + + onStack.remove(u) + // Post-order: add to result after all descendants are processed. + result = append(result, nodeMap.Value(u)) + } + + // Visit every node in ascending order to ensure stability. + for u := range numNodes { + visit(u) + } + + return result +} + +// ReversePostorder returns the nodes of the graph in reverse post-order. +// +// If g is a directed acyclic graph (DAG), the result is a topological sort of +// g. +// +// See [Postorder] for how this handles back-edges and cycles. +// +// This algorithm runs in O(V + E) time and O(V + E) space. +func ReversePostorder[NodeID comparable](g Graph[NodeID]) []NodeID { + result := Postorder(g) + slices.Reverse(result) + return result +} + +// bitset is a simple fixed-size bitset used to reduce memory overhead. +type bitset []uint64 + +func newBitset(n int) bitset { + return make(bitset, (n+63)/64) +} + +func (b bitset) add(u int) bool { + if b.contains(u) { + return false + } + b[u/64] |= 1 << (u % 64) + return true +} + +func (b bitset) remove(u int) { + b[u/64] &= ^(1 << (u % 64)) +} + +func (b bitset) contains(u int) bool { + return b[u/64]&(1<<(u%64)) != 0 +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/graph/reachable.go b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/reachable.go new file mode 100644 index 00000000..dd14eae8 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/reachable.go @@ -0,0 +1,23 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package graph + +// Reachable returns the set of nodes reachable from the given roots. +func Reachable[NodeID comparable](g Graph[NodeID], roots ...NodeID) map[NodeID]bool { + seen := make(map[NodeID]bool) + var visit func(node NodeID) + visit = func(node NodeID) { + if !seen[node] { + seen[node] = true + for e := range g.Out(node) { + visit(e) + } + } + } + for _, root := range roots { + visit(root) + } + return seen +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/graph/scc.go b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/scc.go new file mode 100644 index 00000000..fb4764f1 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/scc.go @@ -0,0 +1,39 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package graph + +import "slices" + +// SCCs computes the strongly connected components of the graph g. +func SCCs[NodeID comparable](g Graph[NodeID]) [][]NodeID { + // Use Kosaraju's algorithm. Tarjan is overkill here. + + // Forward pass + S := Postorder(g) + + // Reverse pass + gt := Transpose(g) + seen := make(map[NodeID]bool) + var scc []NodeID + var sccs [][]NodeID + var rvisit func(NodeID) + rvisit = func(u NodeID) { + if !seen[u] { + seen[u] = true + scc = append(scc, u) + for v := range gt.Out(u) { + rvisit(v) + } + } + } + for _, root := range slices.Backward(S) { + if !seen[root] { + scc = nil + rvisit(root) + sccs = append(sccs, scc) + } + } + return sccs +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/graph/shortest.go b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/shortest.go new file mode 100644 index 00000000..e7457e8b --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/shortest.go @@ -0,0 +1,45 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package graph + +import "slices" + +// ShortestPath returns a shortest path from src to dst in g. +// It returns the path as a slice of nodes starting with src and ending with dst. +// If no path is found, it returns nil. +func ShortestPath[NodeID comparable](g Graph[NodeID], src, dst NodeID) []NodeID { + if src == dst { + return []NodeID{src} + } + + pred := make(map[NodeID]NodeID) + queue := []NodeID{src} + // Mark src as seen. + pred[src] = src + + for len(queue) > 0 { + n := queue[0] + queue = queue[1:] + + if n == dst { + // Reconstruct path + var path []NodeID + for curr := dst; curr != src; curr = pred[curr] { + path = append(path, curr) + } + path = append(path, src) + slices.Reverse(path) + return path + } + + for v := range g.Out(n) { + if _, seen := pred[v]; !seen { + pred[v] = n + queue = append(queue, v) + } + } + } + return nil +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/graph/transpose.go b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/transpose.go new file mode 100644 index 00000000..431fc5f6 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/graph/transpose.go @@ -0,0 +1,51 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package graph + +import ( + "iter" + "slices" +) + +type transpose[NodeID comparable] struct { + Graph Graph[NodeID] + preds map[NodeID][]NodeID +} + +// transpose returns a graph like g but with all edges reversed. Node IDs are +// identical to the underlying graph. +// +// Transpose preserves compactness. +func Transpose[NodeID comparable](g Graph[NodeID]) Graph[NodeID] { + if g, ok := g.(transpose[NodeID]); ok { + // Transpose(Transpose(g)) == g + return g.Graph + } + + preds := make(map[NodeID][]NodeID) + for nid := range g.Nodes() { + for succ := range g.Out(nid) { + preds[succ] = append(preds[succ], nid) + } + } + return transpose[NodeID]{g, preds} +} + +func (t transpose[NodeID]) NumNodes() int { + return len(t.preds) +} + +func (t transpose[NodeID]) Nodes() iter.Seq[NodeID] { + return t.Graph.Nodes() +} + +func (t transpose[NodeID]) Out(n NodeID) iter.Seq[NodeID] { + return slices.Values(t.preds[n]) +} + +//lint:ignore U1000 False positive in Staticcheck 2026.1 and older. +func (t transpose[NodeID]) unwrapPreservingNodes() Graph[NodeID] { + return t.Graph +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/moreiters/iters.go b/vendor/honnef.co/go/tools/internal/xtools-internal/moreiters/iters.go new file mode 100644 index 00000000..3dc81c4d --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/moreiters/iters.go @@ -0,0 +1,63 @@ +// Copyright 2025 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 moreiters + +import "iter" + +// First returns the first value of seq and true. +// If seq is empty, it returns the zero value of T and false. +func First[T any](seq iter.Seq[T]) (z T, ok bool) { + for t := range seq { + return t, true + } + return z, false +} + +// Contains reports whether x is an element of the sequence seq. +func Contains[T comparable](seq iter.Seq[T], x T) bool { + for cand := range seq { + if cand == x { + return true + } + } + return false +} + +// Every reports whether every pred(t) for t in seq returns true, +// stopping at the first false element. +func Every[T any](seq iter.Seq[T], pred func(T) bool) bool { + for t := range seq { + if !pred(t) { + return false + } + } + return true +} + +// Any reports whether any pred(t) for t in seq returns true. +func Any[T any](seq iter.Seq[T], pred func(T) bool) bool { + for t := range seq { + if pred(t) { + return true + } + } + return false +} + +// Len returns the number of elements in the sequence (by iterating). +func Len[T any](seq iter.Seq[T]) (n int) { + for range seq { + n++ + } + return +} + +// Empty reports whether the sequence contains no elements. +func Empty[T any](seq iter.Seq[T]) bool { + for range seq { + return false + } + return true +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/common.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/common.go new file mode 100644 index 00000000..cdae2b8e --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/common.go @@ -0,0 +1,68 @@ +// Copyright 2021 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 typeparams contains common utilities for writing tools that +// interact with generic Go code, as introduced with Go 1.18. It +// supplements the standard library APIs. Notably, the StructuralTerms +// API computes a minimal representation of the structural +// restrictions on a type parameter. +// +// An external version of these APIs is available in the +// golang.org/x/exp/typeparams module. +package typeparams + +import ( + "go/ast" + "go/token" + "go/types" +) + +// UnpackIndexExpr extracts data from AST nodes that represent index +// expressions. +// +// For an ast.IndexExpr, the resulting indices slice will contain exactly one +// index expression. For an ast.IndexListExpr (go1.18+), it may have a variable +// number of index expressions. +// +// For nodes that don't represent index expressions, the first return value of +// UnpackIndexExpr will be nil. +func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) { + switch e := n.(type) { + case *ast.IndexExpr: + return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack + case *ast.IndexListExpr: + return e.X, e.Lbrack, e.Indices, e.Rbrack + } + return nil, token.NoPos, nil, token.NoPos +} + +// PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on +// the cardinality of indices. Calling PackIndexExpr with len(indices) == 0 +// will panic. +func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr { + switch len(indices) { + case 0: + panic("empty indices") + case 1: + return &ast.IndexExpr{ + X: x, + Lbrack: lbrack, + Index: indices[0], + Rbrack: rbrack, + } + default: + return &ast.IndexListExpr{ + X: x, + Lbrack: lbrack, + Indices: indices, + Rbrack: rbrack, + } + } +} + +// IsTypeParam reports whether t is a type parameter (or an alias of one). +func IsTypeParam(t types.Type) bool { + _, ok := types.Unalias(t).(*types.TypeParam) + return ok +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/coretype.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/coretype.go new file mode 100644 index 00000000..2e05de46 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/coretype.go @@ -0,0 +1,157 @@ +// 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 typeparams + +import ( + "fmt" + "go/types" +) + +// CoreType returns the core type of T or nil if T does not have a core type. +// +// As of Go1.25, the notion of a core type has been removed from the language spec. +// See https://go.dev/blog/coretypes for more details. +// TODO(mkalil): We should eventually consider removing all uses of CoreType. +func CoreType(T types.Type) types.Type { + U := T.Underlying() + if _, ok := U.(*types.Interface); !ok { + return U // for non-interface types, + } + + terms, err := NormalTerms(U) + if len(terms) == 0 || err != nil { + // len(terms) -> empty type set of interface. + // err != nil => U is invalid, exceeds complexity bounds, or has an empty type set. + return nil // no core type. + } + + U = terms[0].Type().Underlying() + var identical int // i in [0,identical) => Identical(U, terms[i].Type().Underlying()) + for identical = 1; identical < len(terms); identical++ { + if !types.Identical(U, terms[identical].Type().Underlying()) { + break + } + } + + if identical == len(terms) { + // From the deprecated core types spec: + // "There is a single type U which is the underlying type of all types in the type set of T" + return U + } + ch, ok := U.(*types.Chan) + if !ok { + return nil // no core type as identical < len(terms) and U is not a channel. + } + // From the deprecated core types spec: + // "the type chan E if T contains only bidirectional channels, or the type chan<- E or + // <-chan E depending on the direction of the directional channels present." + for chans := identical; chans < len(terms); chans++ { + curr, ok := terms[chans].Type().Underlying().(*types.Chan) + if !ok { + return nil + } + if !types.Identical(ch.Elem(), curr.Elem()) { + return nil // channel elements are not identical. + } + if ch.Dir() == types.SendRecv { + // ch is bidirectional. We can safely always use curr's direction. + ch = curr + } else if curr.Dir() != types.SendRecv && ch.Dir() != curr.Dir() { + // ch and curr are not bidirectional and not the same direction. + return nil + } + } + return ch +} + +// NormalTerms returns a slice of terms representing the normalized structural +// type restrictions of a type, if any. +// +// For all types other than *types.TypeParam, *types.Interface, and +// *types.Union, this is just a single term with Tilde() == false and +// Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see +// below. +// +// Structural type restrictions of a type parameter are created via +// non-interface types embedded in its constraint interface (directly, or via a +// chain of interface embeddings). For example, in the declaration type +// T[P interface{~int; m()}] int the structural restriction of the type +// parameter P is ~int. +// +// With interface embedding and unions, the specification of structural type +// restrictions may be arbitrarily complex. For example, consider the +// following: +// +// type A interface{ ~string|~[]byte } +// +// type B interface{ int|string } +// +// type C interface { ~string|~int } +// +// type T[P interface{ A|B; C }] int +// +// In this example, the structural type restriction of P is ~string|int: A|B +// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, +// which when intersected with C (~string|~int) yields ~string|int. +// +// NormalTerms computes these expansions and reductions, producing a +// "normalized" form of the embeddings. A structural restriction is normalized +// if it is a single union containing no interface terms, and is minimal in the +// sense that removing any term changes the set of types satisfying the +// constraint. It is left as a proof for the reader that, modulo sorting, there +// is exactly one such normalized form. +// +// Because the minimal representation always takes this form, NormalTerms +// returns a slice of tilde terms corresponding to the terms of the union in +// the normalized structural restriction. An error is returned if the type is +// invalid, exceeds complexity bounds, or has an empty type set. In the latter +// case, NormalTerms returns ErrEmptyTypeSet. +// +// NormalTerms makes no guarantees about the order of terms, except that it +// is deterministic. +func NormalTerms(T types.Type) ([]*types.Term, error) { + // typeSetOf(T) == typeSetOf(Unalias(T)) + typ := types.Unalias(T) + if named, ok := typ.(*types.Named); ok { + typ = named.Underlying() + } + switch typ := typ.(type) { + case *types.TypeParam: + return StructuralTerms(typ) + case *types.Union: + return UnionTermSet(typ) + case *types.Interface: + return InterfaceTermSet(typ) + default: + return []*types.Term{types.NewTerm(false, T)}, nil + } +} + +// Deref returns the type of the variable pointed to by t, +// if t's core type is a pointer; otherwise it returns t. +// +// Do not assume that Deref(T)==T implies T is not a pointer: +// consider "type T *T", for example. +// +// TODO(adonovan): ideally this would live in typesinternal, but that +// creates an import cycle. Move there when we melt this package down. +func Deref(t types.Type) types.Type { + if ptr, ok := CoreType(t).(*types.Pointer); ok { + return ptr.Elem() + } + return t +} + +// MustDeref returns the type of the variable pointed to by t. +// It panics if t's core type is not a pointer. +// +// TODO(adonovan): ideally this would live in typesinternal, but that +// creates an import cycle. Move there when we melt this package down. +func MustDeref(t types.Type) types.Type { + if ptr, ok := CoreType(t).(*types.Pointer); ok { + return ptr.Elem() + } + panic(fmt.Sprintf("%v is not a pointer", t)) +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/free.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/free.go new file mode 100644 index 00000000..4c391876 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/free.go @@ -0,0 +1,129 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typeparams + +import ( + "go/types" +) + +// Free is a memoization of the set of free type parameters within a +// type. It makes a sequence of calls to [Free.Has] for overlapping +// types more efficient. The zero value is ready for use. +// +// NOTE: Adapted from go/types/infer.go. If it is later exported, factor. +type Free struct { + seen map[types.Type]bool +} + +// Has reports whether the specified type has a free type parameter. +func (w *Free) Has(typ types.Type) (res bool) { + // detect cycles + if x, ok := w.seen[typ]; ok { + return x + } + if w.seen == nil { + w.seen = make(map[types.Type]bool) + } + w.seen[typ] = false + defer func() { + w.seen[typ] = res + }() + + switch t := typ.(type) { + case nil, *types.Basic: // TODO(gri) should nil be handled here? + break + + case *types.Alias: + if t.TypeParams().Len() > t.TypeArgs().Len() { + return true // This is an uninstantiated Alias. + } + // The expansion of an alias can have free type parameters, + // whether or not the alias itself has type parameters: + // + // func _[K comparable]() { + // type Set = map[K]bool // free(Set) = {K} + // type MapTo[V] = map[K]V // free(Map[foo]) = {V} + // } + // + // So, we must Unalias. + return w.Has(types.Unalias(t)) + + case *types.Array: + return w.Has(t.Elem()) + + case *types.Slice: + return w.Has(t.Elem()) + + case *types.Struct: + for i, n := 0, t.NumFields(); i < n; i++ { + if w.Has(t.Field(i).Type()) { + return true + } + } + + case *types.Pointer: + return w.Has(t.Elem()) + + case *types.Tuple: + n := t.Len() + for i := range n { + if w.Has(t.At(i).Type()) { + return true + } + } + + case *types.Signature: + // t.tparams may not be nil if we are looking at a signature + // of a generic function type (or an interface method) that is + // part of the type we're testing. We don't care about these type + // parameters. + // Similarly, the receiver of a method may declare (rather than + // use) type parameters, we don't care about those either. + // Thus, we only need to look at the input and result parameters. + return w.Has(t.Params()) || w.Has(t.Results()) + + case *types.Interface: + for i, n := 0, t.NumMethods(); i < n; i++ { + if w.Has(t.Method(i).Type()) { + return true + } + } + terms, err := InterfaceTermSet(t) + if err != nil { + return false // ill typed + } + for _, term := range terms { + if w.Has(term.Type()) { + return true + } + } + + case *types.Map: + return w.Has(t.Key()) || w.Has(t.Elem()) + + case *types.Chan: + return w.Has(t.Elem()) + + case *types.Named: + args := t.TypeArgs() + if params := t.TypeParams(); params.Len() > args.Len() { + return true // this is an uninstantiated named type. + } + for i, n := 0, args.Len(); i < n; i++ { + if w.Has(args.At(i)) { + return true + } + } + return w.Has(t.Underlying()) // recurse for types local to parameterized functions + + case *types.TypeParam: + return true + + default: + panic(t) // unreachable + } + + return false +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/normalize.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/normalize.go new file mode 100644 index 00000000..ba367871 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/normalize.go @@ -0,0 +1,214 @@ +// Copyright 2021 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 typeparams + +import ( + "errors" + "fmt" + "go/types" + "os" + "strings" +) + +const debug = false + +var ErrEmptyTypeSet = errors.New("empty type set") + +// StructuralTerms returns a slice of terms representing the normalized +// structural type restrictions of a type parameter, if any. +// +// Structural type restrictions of a type parameter are created via +// non-interface types embedded in its constraint interface (directly, or via a +// chain of interface embeddings). For example, in the declaration +// +// type T[P interface{~int; m()}] int +// +// the structural restriction of the type parameter P is ~int. +// +// With interface embedding and unions, the specification of structural type +// restrictions may be arbitrarily complex. For example, consider the +// following: +// +// type A interface{ ~string|~[]byte } +// +// type B interface{ int|string } +// +// type C interface { ~string|~int } +// +// type T[P interface{ A|B; C }] int +// +// In this example, the structural type restriction of P is ~string|int: A|B +// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, +// which when intersected with C (~string|~int) yields ~string|int. +// +// StructuralTerms computes these expansions and reductions, producing a +// "normalized" form of the embeddings. A structural restriction is normalized +// if it is a single union containing no interface terms, and is minimal in the +// sense that removing any term changes the set of types satisfying the +// constraint. It is left as a proof for the reader that, modulo sorting, there +// is exactly one such normalized form. +// +// Because the minimal representation always takes this form, StructuralTerms +// returns a slice of tilde terms corresponding to the terms of the union in +// the normalized structural restriction. An error is returned if the +// constraint interface is invalid, exceeds complexity bounds, or has an empty +// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet. +// +// StructuralTerms makes no guarantees about the order of terms, except that it +// is deterministic. +func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) { + constraint := tparam.Constraint() + if constraint == nil { + return nil, fmt.Errorf("%s has nil constraint", tparam) + } + iface, _ := constraint.Underlying().(*types.Interface) + if iface == nil { + return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying()) + } + return InterfaceTermSet(iface) +} + +// InterfaceTermSet computes the normalized terms for a constraint interface, +// returning an error if the term set cannot be computed or is empty. In the +// latter case, the error will be ErrEmptyTypeSet. +// +// See the documentation of StructuralTerms for more information on +// normalization. +func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) { + return computeTermSet(iface) +} + +// UnionTermSet computes the normalized terms for a union, returning an error +// if the term set cannot be computed or is empty. In the latter case, the +// error will be ErrEmptyTypeSet. +// +// See the documentation of StructuralTerms for more information on +// normalization. +func UnionTermSet(union *types.Union) ([]*types.Term, error) { + return computeTermSet(union) +} + +func computeTermSet(typ types.Type) ([]*types.Term, error) { + tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0) + if err != nil { + return nil, err + } + if tset.terms.isEmpty() { + return nil, ErrEmptyTypeSet + } + if tset.terms.isAll() { + return nil, nil + } + var terms []*types.Term + for _, term := range tset.terms { + terms = append(terms, types.NewTerm(term.tilde, term.typ)) + } + return terms, nil +} + +// A termSet holds the normalized set of terms for a given type. +// +// The name termSet is intentionally distinct from 'type set': a type set is +// all types that implement a type (and includes method restrictions), whereas +// a term set just represents the structural restrictions on a type. +type termSet struct { + complete bool + terms termlist +} + +func indentf(depth int, format string, args ...any) { + fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...) +} + +func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) { + if t == nil { + panic("nil type") + } + + if debug { + indentf(depth, "%s", t.String()) + defer func() { + if err != nil { + indentf(depth, "=> %s", err) + } else { + indentf(depth, "=> %s", res.terms.String()) + } + }() + } + + const maxTermCount = 100 + if tset, ok := seen[t]; ok { + if !tset.complete { + return nil, fmt.Errorf("cycle detected in the declaration of %s", t) + } + return tset, nil + } + + // Mark the current type as seen to avoid infinite recursion. + tset := new(termSet) + defer func() { + tset.complete = true + }() + seen[t] = tset + + switch u := t.Underlying().(type) { + case *types.Interface: + // The term set of an interface is the intersection of the term sets of its + // embedded types. + tset.terms = allTermlist + for embedded := range u.EmbeddedTypes() { + if _, ok := embedded.Underlying().(*types.TypeParam); ok { + return nil, fmt.Errorf("invalid embedded type %T", embedded) + } + tset2, err := computeTermSetInternal(embedded, seen, depth+1) + if err != nil { + return nil, err + } + tset.terms = tset.terms.intersect(tset2.terms) + } + case *types.Union: + // The term set of a union is the union of term sets of its terms. + tset.terms = nil + for t := range u.Terms() { + var terms termlist + switch t.Type().Underlying().(type) { + case *types.Interface: + tset2, err := computeTermSetInternal(t.Type(), seen, depth+1) + if err != nil { + return nil, err + } + terms = tset2.terms + case *types.TypeParam, *types.Union: + // A stand-alone type parameter or union is not permitted as union + // term. + return nil, fmt.Errorf("invalid union term %T", t) + default: + if t.Type() == types.Typ[types.Invalid] { + continue + } + terms = termlist{{t.Tilde(), t.Type()}} + } + tset.terms = tset.terms.union(terms) + if len(tset.terms) > maxTermCount { + return nil, fmt.Errorf("exceeded max term count %d", maxTermCount) + } + } + case *types.TypeParam: + panic("unreachable") + default: + // For all other types, the term set is just a single non-tilde term + // holding the type itself. + if u != types.Typ[types.Invalid] { + tset.terms = termlist{{false, t}} + } + } + return tset, nil +} + +// under is a facade for the go/types internal function of the same name. It is +// used by typeterm.go. +func under(t types.Type) types.Type { + return t.Underlying() +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/termlist.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/termlist.go new file mode 100644 index 00000000..9bc29143 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/termlist.go @@ -0,0 +1,169 @@ +// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT. +// Source: ../../cmd/compile/internal/types2/termlist.go + +// Copyright 2021 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 copytermlist.go DO NOT EDIT. + +package typeparams + +import ( + "go/types" + "strings" +) + +// A termlist represents the type set represented by the union +// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn. +// A termlist is in normal form if all terms are disjoint. +// termlist operations don't require the operands to be in +// normal form. +type termlist []*term + +// allTermlist represents the set of all types. +// It is in normal form. +var allTermlist = termlist{new(term)} + +// termSep is the separator used between individual terms. +const termSep = " | " + +// String prints the termlist exactly (without normalization). +func (xl termlist) String() string { + if len(xl) == 0 { + return "∅" + } + var buf strings.Builder + for i, x := range xl { + if i > 0 { + buf.WriteString(termSep) + } + buf.WriteString(x.String()) + } + return buf.String() +} + +// isEmpty reports whether the termlist xl represents the empty set of types. +func (xl termlist) isEmpty() bool { + // If there's a non-nil term, the entire list is not empty. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil { + return false + } + } + return true +} + +// isAll reports whether the termlist xl represents the set of all types. +func (xl termlist) isAll() bool { + // If there's a 𝓤 term, the entire list is 𝓤. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil && x.typ == nil { + return true + } + } + return false +} + +// norm returns the normal form of xl. +func (xl termlist) norm() termlist { + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + used := make([]bool, len(xl)) + var rl termlist + for i, xi := range xl { + if xi == nil || used[i] { + continue + } + for j := i + 1; j < len(xl); j++ { + xj := xl[j] + if xj == nil || used[j] { + continue + } + if u1, u2 := xi.union(xj); u2 == nil { + // If we encounter a 𝓤 term, the entire list is 𝓤. + // Exit early. + // (Note that this is not just an optimization; + // if we continue, we may end up with a 𝓤 term + // and other terms and the result would not be + // in normal form.) + if u1.typ == nil { + return allTermlist + } + xi = u1 + used[j] = true // xj is now unioned into xi - ignore it in future iterations + } + } + rl = append(rl, xi) + } + return rl +} + +// union returns the union xl ∪ yl. +func (xl termlist) union(yl termlist) termlist { + return append(xl, yl...).norm() +} + +// intersect returns the intersection xl ∩ yl. +func (xl termlist) intersect(yl termlist) termlist { + if xl.isEmpty() || yl.isEmpty() { + return nil + } + + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + var rl termlist + for _, x := range xl { + for _, y := range yl { + if r := x.intersect(y); r != nil { + rl = append(rl, r) + } + } + } + return rl.norm() +} + +// equal reports whether xl and yl represent the same type set. +func (xl termlist) equal(yl termlist) bool { + // TODO(gri) this should be more efficient + return xl.subsetOf(yl) && yl.subsetOf(xl) +} + +// includes reports whether t ∈ xl. +func (xl termlist) includes(t types.Type) bool { + for _, x := range xl { + if x.includes(t) { + return true + } + } + return false +} + +// supersetOf reports whether y ⊆ xl. +func (xl termlist) supersetOf(y *term) bool { + for _, x := range xl { + if y.subsetOf(x) { + return true + } + } + return false +} + +// subsetOf reports whether xl ⊆ yl. +func (xl termlist) subsetOf(yl termlist) bool { + if yl.isEmpty() { + return xl.isEmpty() + } + + // each term x of xl must be a subset of yl + for _, x := range xl { + if !yl.supersetOf(x) { + return false // x is not a subset yl + } + } + return true +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/typeterm.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/typeterm.go new file mode 100644 index 00000000..fa758cdc --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typeparams/typeterm.go @@ -0,0 +1,172 @@ +// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT. +// Source: ../../cmd/compile/internal/types2/typeterm.go + +// Copyright 2021 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 copytermlist.go DO NOT EDIT. + +package typeparams + +import "go/types" + +// A term describes elementary type sets: +// +// ∅: (*term)(nil) == ∅ // set of no types (empty set) +// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) +// T: &term{false, T} == {T} // set of type T +// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t +type term struct { + tilde bool // valid if typ != nil + typ types.Type +} + +func (x *term) String() string { + switch { + case x == nil: + return "∅" + case x.typ == nil: + return "𝓤" + case x.tilde: + return "~" + x.typ.String() + default: + return x.typ.String() + } +} + +// equal reports whether x and y represent the same type set. +func (x *term) equal(y *term) bool { + // easy cases + switch { + case x == nil || y == nil: + return x == y + case x.typ == nil || y.typ == nil: + return x.typ == y.typ + } + // ∅ ⊂ x, y ⊂ 𝓤 + + return x.tilde == y.tilde && types.Identical(x.typ, y.typ) +} + +// union returns the union x ∪ y: zero, one, or two non-nil terms. +func (x *term) union(y *term) (_, _ *term) { + // easy cases + switch { + case x == nil && y == nil: + return nil, nil // ∅ ∪ ∅ == ∅ + case x == nil: + return y, nil // ∅ ∪ y == y + case y == nil: + return x, nil // x ∪ ∅ == x + case x.typ == nil: + return x, nil // 𝓤 ∪ y == 𝓤 + case y.typ == nil: + return y, nil // x ∪ 𝓤 == 𝓤 + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return x, y // x ∪ y == (x, y) if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ∪ ~t == ~t + // ~t ∪ T == ~t + // T ∪ ~t == ~t + // T ∪ T == T + if x.tilde || !y.tilde { + return x, nil + } + return y, nil +} + +// intersect returns the intersection x ∩ y. +func (x *term) intersect(y *term) *term { + // easy cases + switch { + case x == nil || y == nil: + return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅ + case x.typ == nil: + return y // 𝓤 ∩ y == y + case y.typ == nil: + return x // x ∩ 𝓤 == x + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return nil // x ∩ y == ∅ if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ∩ ~t == ~t + // ~t ∩ T == T + // T ∩ ~t == T + // T ∩ T == T + if !x.tilde || y.tilde { + return x + } + return y +} + +// includes reports whether t ∈ x. +func (x *term) includes(t types.Type) bool { + // easy cases + switch { + case x == nil: + return false // t ∈ ∅ == false + case x.typ == nil: + return true // t ∈ 𝓤 == true + } + // ∅ ⊂ x ⊂ 𝓤 + + u := t + if x.tilde { + u = under(u) + } + return types.Identical(x.typ, u) +} + +// subsetOf reports whether x ⊆ y. +func (x *term) subsetOf(y *term) bool { + // easy cases + switch { + case x == nil: + return true // ∅ ⊆ y == true + case y == nil: + return false // x ⊆ ∅ == false since x != ∅ + case y.typ == nil: + return true // x ⊆ 𝓤 == true + case x.typ == nil: + return false // 𝓤 ⊆ y == false since y != 𝓤 + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return false // x ⊆ y == false if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ⊆ ~t == true + // ~t ⊆ T == false + // T ⊆ ~t == true + // T ⊆ T == true + return !x.tilde || y.tilde +} + +// disjoint reports whether x ∩ y == ∅. +// x.typ and y.typ must not be nil. +func (x *term) disjoint(y *term) bool { + if debug && (x.typ == nil || y.typ == nil) { + panic("invalid argument(s)") + } + ux := x.typ + if y.tilde { + ux = under(ux) + } + uy := y.typ + if x.tilde { + uy = under(uy) + } + return !types.Identical(ux, uy) +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/classify_call.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/classify_call.go new file mode 100644 index 00000000..7ebe9768 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/classify_call.go @@ -0,0 +1,137 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typesinternal + +import ( + "fmt" + "go/ast" + "go/types" + _ "unsafe" // for go:linkname hack +) + +// CallKind describes the function position of an [*ast.CallExpr]. +type CallKind int + +const ( + CallStatic CallKind = iota // static call to known function + CallInterface // dynamic call through an interface method + CallDynamic // dynamic call of a func value + CallBuiltin // call to a builtin function + CallConversion // a conversion (not a call) +) + +var callKindNames = []string{ + "CallStatic", + "CallInterface", + "CallDynamic", + "CallBuiltin", + "CallConversion", +} + +func (k CallKind) String() string { + if i := int(k); i >= 0 && i < len(callKindNames) { + return callKindNames[i] + } + return fmt.Sprintf("typeutil.CallKind(%d)", k) +} + +// ClassifyCall classifies the function position of a call expression ([*ast.CallExpr]). +// It distinguishes among true function calls, calls to builtins, and type conversions, +// and further classifies function calls as static calls (where the function is known), +// dynamic interface calls, and other dynamic calls. +// +// For the declarations: +// +// func f() {} +// func g[T any]() {} +// var v func() +// var s []func() +// type I interface { M() } +// var i I +// +// ClassifyCall returns the following: +// +// f() CallStatic +// g[int]() CallStatic +// i.M() CallInterface +// min(1, 2) CallBuiltin +// v() CallDynamic +// s[0]() CallDynamic +// int(x) CallConversion +// []byte("") CallConversion +func ClassifyCall(info *types.Info, call *ast.CallExpr) CallKind { + if info.Types == nil { + panic("ClassifyCall: info.Types is nil") + } + tv := info.Types[call.Fun] + if tv.IsType() { + return CallConversion + } + if tv.IsBuiltin() { + return CallBuiltin + } + obj := info.Uses[UsedIdent(info, call.Fun)] + // Classify the call by the type of the object, if any. + switch obj := obj.(type) { + case *types.Func: + if interfaceMethod(obj) { + return CallInterface + } + return CallStatic + default: + return CallDynamic + } +} + +// UsedIdent returns the identifier such that info.Uses[UsedIdent(info, e)] +// is the [types.Object] used by e, if any. +// +// If e is one of various forms of reference: +// +// f, c, v, T lexical reference +// pkg.X qualified identifier +// f[T] or pkg.F[K,V] instantiations of the above kinds +// expr.f field or method value selector +// T.f method expression selector +// +// UsedIdent returns the identifier whose is associated value in [types.Info.Uses] +// is the object to which it refers. +// +// For the declarations: +// +// func F[T any] {...} +// type I interface { M() } +// var ( +// x int +// s struct { f int } +// a []int +// i I +// ) +// +// UsedIdent returns the following: +// +// Expr UsedIdent +// x x +// s.f f +// F[int] F +// i.M M +// I.M M +// min min +// int int +// 1 nil +// a[0] nil +// []byte nil +// +// Note: if e is an instantiated function or method, UsedIdent returns +// the corresponding generic function or method on the generic type. +func UsedIdent(info *types.Info, e ast.Expr) *ast.Ident { + return usedIdent(info, e) +} + +//go:linkname usedIdent golang.org/x/tools/go/types/typeutil.usedIdent +func usedIdent(info *types.Info, e ast.Expr) *ast.Ident + +//go:linkname interfaceMethod golang.org/x/tools/go/types/typeutil.interfaceMethod +func interfaceMethod(f *types.Func) bool diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/element.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/element.go new file mode 100644 index 00000000..89eeea16 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/element.go @@ -0,0 +1,137 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typesinternal + +import ( + "fmt" + "go/types" + + "golang.org/x/tools/go/types/typeutil" +) + +// ForEachElement calls f for type T and each type reachable from its +// type through reflection. It does this by recursively stripping off +// type constructors; in addition, for each named type N, the type *N +// is added to the result as it may have additional methods. +// +// The caller must provide an initially empty set used to de-duplicate +// identical types, potentially across multiple calls to ForEachElement. +// (Its final value holds all the elements seen, matching the arguments +// passed to f.) +// +// TODO(adonovan): share/harmonize with go/callgraph/rta. +func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T types.Type, f func(types.Type)) { + var visit func(T types.Type, skip bool) + visit = func(T types.Type, skip bool) { + if !skip { + if seen, _ := rtypes.Set(T, true).(bool); seen { + return // de-dup + } + + f(T) // notify caller of new element type + } + + // Recursion over signatures of each method. + tmset := msets.MethodSet(T) + for method := range tmset.Methods() { + sig := method.Type().(*types.Signature) + if sig.TypeParams() != nil { + continue // skip type-parameterized methods + } + + // It is tempting to call visit(sig, false) + // but, as noted in golang.org/cl/65450043, + // the Signature.Recv field is ignored by + // types.Identical and typeutil.Map, which + // is confusing at best. + // + // More importantly, the true signature rtype + // reachable from a method using reflection + // has no receiver but an extra ordinary parameter. + // For the Read method of io.Reader we want: + // func(Reader, []byte) (int, error) + // but here sig is: + // func([]byte) (int, error) + // with .Recv = Reader (though it is hard to + // notice because it doesn't affect Signature.String + // or types.Identical). + // + // TODO(adonovan): construct and visit the correct + // non-method signature with an extra parameter + // (though since unnamed func types have no methods + // there is essentially no actual demand for this). + // + // TODO(adonovan): document whether or not it is + // safe to skip non-exported methods (as RTA does). + visit(sig.Params(), true) // skip the Tuple + visit(sig.Results(), true) // skip the Tuple + } + + switch T := T.(type) { + case *types.Alias: + visit(types.Unalias(T), skip) // emulates the pre-Alias behavior + + case *types.Basic: + // nop + + case *types.Interface: + // nop---handled by recursion over method set. + + case *types.Pointer: + visit(T.Elem(), false) + + case *types.Slice: + visit(T.Elem(), false) + + case *types.Chan: + visit(T.Elem(), false) + + case *types.Map: + visit(T.Key(), false) + visit(T.Elem(), false) + + case *types.Signature: + if T.Recv() != nil { + panic(fmt.Sprintf("Signature %s has Recv %s", T, T.Recv())) + } + visit(T.Params(), true) // skip the Tuple + visit(T.Results(), true) // skip the Tuple + + case *types.Named: + // A pointer-to-named type can be derived from a named + // type via reflection. It may have methods too. + visit(types.NewPointer(T), false) + + // Consider 'type T struct{S}' where S has methods. + // Reflection provides no way to get from T to struct{S}, + // only to S, so the method set of struct{S} is unwanted, + // so set 'skip' flag during recursion. + visit(T.Underlying(), true) // skip the unnamed type + + case *types.Array: + visit(T.Elem(), false) + + case *types.Struct: + for i, n := 0, T.NumFields(); i < n; i++ { + // TODO(adonovan): document whether or not + // it is safe to skip non-exported fields. + visit(T.Field(i).Type(), false) + } + + case *types.Tuple: + for i, n := 0, T.Len(); i < n; i++ { + visit(T.At(i).Type(), false) + } + + case *types.TypeParam, *types.Union: + // forEachReachable must not be called on parameterized types. + panic(fmt.Sprintf("ForEachElement called on type containing %T", T)) + + default: + panic(fmt.Sprintf("ForEachElement called on unexpected type %T", T)) + } + } + visit(T, false) +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/errorcode.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/errorcode.go new file mode 100644 index 00000000..bd944741 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/errorcode.go @@ -0,0 +1,1558 @@ +// 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 typesinternal + +type ErrorCode int + +// This file defines the error codes that can be produced during type-checking. +// Collectively, these codes provide an identifier that may be used to +// implement special handling for certain types of errors. +// +// Error codes should be fine-grained enough that the exact nature of the error +// can be easily determined, but coarse enough that they are not an +// implementation detail of the type checking algorithm. As a rule-of-thumb, +// errors should be considered equivalent if there is a theoretical refactoring +// of the type checker in which they are emitted in exactly one place. For +// example, the type checker emits different error messages for "too many +// arguments" and "too few arguments", but one can imagine an alternative type +// checker where this check instead just emits a single "wrong number of +// arguments", so these errors should have the same code. +// +// Error code names should be as brief as possible while retaining accuracy and +// distinctiveness. In most cases names should start with an adjective +// describing the nature of the error (e.g. "invalid", "unused", "misplaced"), +// and end with a noun identifying the relevant language object. For example, +// "DuplicateDecl" or "InvalidSliceExpr". For brevity, naming follows the +// convention that "bad" implies a problem with syntax, and "invalid" implies a +// problem with types. + +const ( + // InvalidSyntaxTree occurs if an invalid syntax tree is provided + // to the type checker. It should never happen. + InvalidSyntaxTree ErrorCode = -1 +) + +const ( + _ ErrorCode = iota + + // Test is reserved for errors that only apply while in self-test mode. + Test + + /* package names */ + + // BlankPkgName occurs when a package name is the blank identifier "_". + // + // Per the spec: + // "The PackageName must not be the blank identifier." + BlankPkgName + + // MismatchedPkgName occurs when a file's package name doesn't match the + // package name already established by other files. + MismatchedPkgName + + // InvalidPkgUse occurs when a package identifier is used outside of a + // selector expression. + // + // Example: + // import "fmt" + // + // var _ = fmt + InvalidPkgUse + + /* imports */ + + // BadImportPath occurs when an import path is not valid. + BadImportPath + + // BrokenImport occurs when importing a package fails. + // + // Example: + // import "amissingpackage" + BrokenImport + + // ImportCRenamed occurs when the special import "C" is renamed. "C" is a + // pseudo-package, and must not be renamed. + // + // Example: + // import _ "C" + ImportCRenamed + + // UnusedImport occurs when an import is unused. + // + // Example: + // import "fmt" + // + // func main() {} + UnusedImport + + /* initialization */ + + // InvalidInitCycle occurs when an invalid cycle is detected within the + // initialization graph. + // + // Example: + // var x int = f() + // + // func f() int { return x } + InvalidInitCycle + + /* decls */ + + // DuplicateDecl occurs when an identifier is declared multiple times. + // + // Example: + // var x = 1 + // var x = 2 + DuplicateDecl + + // InvalidDeclCycle occurs when a declaration cycle is not valid. + // + // Example: + // import "unsafe" + // + // type T struct { + // a [n]int + // } + // + // var n = unsafe.Sizeof(T{}) + InvalidDeclCycle + + // InvalidTypeCycle occurs when a cycle in type definitions results in a + // type that is not well-defined. + // + // Example: + // import "unsafe" + // + // type T [unsafe.Sizeof(T{})]int + InvalidTypeCycle + + /* decls > const */ + + // InvalidConstInit occurs when a const declaration has a non-constant + // initializer. + // + // Example: + // var x int + // const _ = x + InvalidConstInit + + // InvalidConstVal occurs when a const value cannot be converted to its + // target type. + // + // TODO(findleyr): this error code and example are not very clear. Consider + // removing it. + // + // Example: + // const _ = 1 << "hello" + InvalidConstVal + + // InvalidConstType occurs when the underlying type in a const declaration + // is not a valid constant type. + // + // Example: + // const c *int = 4 + InvalidConstType + + /* decls > var (+ other variable assignment codes) */ + + // UntypedNilUse occurs when the predeclared (untyped) value nil is used to + // initialize a variable declared without an explicit type. + // + // Example: + // var x = nil + UntypedNilUse + + // WrongAssignCount occurs when the number of values on the right-hand side + // of an assignment or initialization expression does not match the number + // of variables on the left-hand side. + // + // Example: + // var x = 1, 2 + WrongAssignCount + + // UnassignableOperand occurs when the left-hand side of an assignment is + // not assignable. + // + // Example: + // func f() { + // const c = 1 + // c = 2 + // } + UnassignableOperand + + // NoNewVar occurs when a short variable declaration (':=') does not declare + // new variables. + // + // Example: + // func f() { + // x := 1 + // x := 2 + // } + NoNewVar + + // MultiValAssignOp occurs when an assignment operation (+=, *=, etc) does + // not have single-valued left-hand or right-hand side. + // + // Per the spec: + // "In assignment operations, both the left- and right-hand expression lists + // must contain exactly one single-valued expression" + // + // Example: + // func f() int { + // x, y := 1, 2 + // x, y += 1 + // return x + y + // } + MultiValAssignOp + + // InvalidIfaceAssign occurs when a value of type T is used as an + // interface, but T does not implement a method of the expected interface. + // + // Example: + // type I interface { + // f() + // } + // + // type T int + // + // var x I = T(1) + InvalidIfaceAssign + + // InvalidChanAssign occurs when a chan assignment is invalid. + // + // Per the spec, a value x is assignable to a channel type T if: + // "x is a bidirectional channel value, T is a channel type, x's type V and + // T have identical element types, and at least one of V or T is not a + // defined type." + // + // Example: + // type T1 chan int + // type T2 chan int + // + // var x T1 + // // Invalid assignment because both types are named + // var _ T2 = x + InvalidChanAssign + + // IncompatibleAssign occurs when the type of the right-hand side expression + // in an assignment cannot be assigned to the type of the variable being + // assigned. + // + // Example: + // var x []int + // var _ int = x + IncompatibleAssign + + // UnaddressableFieldAssign occurs when trying to assign to a struct field + // in a map value. + // + // Example: + // func f() { + // m := make(map[string]struct{i int}) + // m["foo"].i = 42 + // } + UnaddressableFieldAssign + + /* decls > type (+ other type expression codes) */ + + // NotAType occurs when the identifier used as the underlying type in a type + // declaration or the right-hand side of a type alias does not denote a type. + // + // Example: + // var S = 2 + // + // type T S + NotAType + + // InvalidArrayLen occurs when an array length is not a constant value. + // + // Example: + // var n = 3 + // var _ = [n]int{} + InvalidArrayLen + + // BlankIfaceMethod occurs when a method name is '_'. + // + // Per the spec: + // "The name of each explicitly specified method must be unique and not + // blank." + // + // Example: + // type T interface { + // _(int) + // } + BlankIfaceMethod + + // IncomparableMapKey occurs when a map key type does not support the == and + // != operators. + // + // Per the spec: + // "The comparison operators == and != must be fully defined for operands of + // the key type; thus the key type must not be a function, map, or slice." + // + // Example: + // var x map[T]int + // + // type T []int + IncomparableMapKey + + // InvalidIfaceEmbed occurs when a non-interface type is embedded in an + // interface. + // + // Example: + // type T struct {} + // + // func (T) m() + // + // type I interface { + // T + // } + InvalidIfaceEmbed + + // InvalidPtrEmbed occurs when an embedded field is of the pointer form *T, + // and T itself is itself a pointer, an unsafe.Pointer, or an interface. + // + // Per the spec: + // "An embedded field must be specified as a type name T or as a pointer to + // a non-interface type name *T, and T itself may not be a pointer type." + // + // Example: + // type T *int + // + // type S struct { + // *T + // } + InvalidPtrEmbed + + /* decls > func and method */ + + // BadRecv occurs when a method declaration does not have exactly one + // receiver parameter. + // + // Example: + // func () _() {} + BadRecv + + // InvalidRecv occurs when a receiver type expression is not of the form T + // or *T, or T is a pointer type. + // + // Example: + // type T struct {} + // + // func (**T) m() {} + InvalidRecv + + // DuplicateFieldAndMethod occurs when an identifier appears as both a field + // and method name. + // + // Example: + // type T struct { + // m int + // } + // + // func (T) m() {} + DuplicateFieldAndMethod + + // DuplicateMethod occurs when two methods on the same receiver type have + // the same name. + // + // Example: + // type T struct {} + // func (T) m() {} + // func (T) m(i int) int { return i } + DuplicateMethod + + /* decls > special */ + + // InvalidBlank occurs when a blank identifier is used as a value or type. + // + // Per the spec: + // "The blank identifier may appear as an operand only on the left-hand side + // of an assignment." + // + // Example: + // var x = _ + InvalidBlank + + // InvalidIota occurs when the predeclared identifier iota is used outside + // of a constant declaration. + // + // Example: + // var x = iota + InvalidIota + + // MissingInitBody occurs when an init function is missing its body. + // + // Example: + // func init() + MissingInitBody + + // InvalidInitSig occurs when an init function declares parameters or + // results. + // + // Example: + // func init() int { return 1 } + InvalidInitSig + + // InvalidInitDecl occurs when init is declared as anything other than a + // function. + // + // Example: + // var init = 1 + InvalidInitDecl + + // InvalidMainDecl occurs when main is declared as anything other than a + // function, in a main package. + InvalidMainDecl + + /* exprs */ + + // TooManyValues occurs when a function returns too many values for the + // expression context in which it is used. + // + // Example: + // func ReturnTwo() (int, int) { + // return 1, 2 + // } + // + // var x = ReturnTwo() + TooManyValues + + // NotAnExpr occurs when a type expression is used where a value expression + // is expected. + // + // Example: + // type T struct {} + // + // func f() { + // T + // } + NotAnExpr + + /* exprs > const */ + + // TruncatedFloat occurs when a float constant is truncated to an integer + // value. + // + // Example: + // var _ int = 98.6 + TruncatedFloat + + // NumericOverflow occurs when a numeric constant overflows its target type. + // + // Example: + // var x int8 = 1000 + NumericOverflow + + /* exprs > operation */ + + // UndefinedOp occurs when an operator is not defined for the type(s) used + // in an operation. + // + // Example: + // var c = "a" - "b" + UndefinedOp + + // MismatchedTypes occurs when operand types are incompatible in a binary + // operation. + // + // Example: + // var a = "hello" + // var b = 1 + // var c = a - b + MismatchedTypes + + // DivByZero occurs when a division operation is provable at compile + // time to be a division by zero. + // + // Example: + // const divisor = 0 + // var x int = 1/divisor + DivByZero + + // NonNumericIncDec occurs when an increment or decrement operator is + // applied to a non-numeric value. + // + // Example: + // func f() { + // var c = "c" + // c++ + // } + NonNumericIncDec + + /* exprs > ptr */ + + // UnaddressableOperand occurs when the & operator is applied to an + // unaddressable expression. + // + // Example: + // var x = &1 + UnaddressableOperand + + // InvalidIndirection occurs when a non-pointer value is indirected via the + // '*' operator. + // + // Example: + // var x int + // var y = *x + InvalidIndirection + + /* exprs > [] */ + + // NonIndexableOperand occurs when an index operation is applied to a value + // that cannot be indexed. + // + // Example: + // var x = 1 + // var y = x[1] + NonIndexableOperand + + // InvalidIndex occurs when an index argument is not of integer type, + // negative, or out-of-bounds. + // + // Example: + // var s = [...]int{1,2,3} + // var x = s[5] + // + // Example: + // var s = []int{1,2,3} + // var _ = s[-1] + // + // Example: + // var s = []int{1,2,3} + // var i string + // var _ = s[i] + InvalidIndex + + // SwappedSliceIndices occurs when constant indices in a slice expression + // are decreasing in value. + // + // Example: + // var _ = []int{1,2,3}[2:1] + SwappedSliceIndices + + /* operators > slice */ + + // NonSliceableOperand occurs when a slice operation is applied to a value + // whose type is not sliceable, or is unaddressable. + // + // Example: + // var x = [...]int{1, 2, 3}[:1] + // + // Example: + // var x = 1 + // var y = 1[:1] + NonSliceableOperand + + // InvalidSliceExpr occurs when a three-index slice expression (a[x:y:z]) is + // applied to a string. + // + // Example: + // var s = "hello" + // var x = s[1:2:3] + InvalidSliceExpr + + /* exprs > shift */ + + // InvalidShiftCount occurs when the right-hand side of a shift operation is + // either non-integer, negative, or too large. + // + // Example: + // var ( + // x string + // y int = 1 << x + // ) + InvalidShiftCount + + // InvalidShiftOperand occurs when the shifted operand is not an integer. + // + // Example: + // var s = "hello" + // var x = s << 2 + InvalidShiftOperand + + /* exprs > chan */ + + // InvalidReceive occurs when there is a channel receive from a value that + // is either not a channel, or is a send-only channel. + // + // Example: + // func f() { + // var x = 1 + // <-x + // } + InvalidReceive + + // InvalidSend occurs when there is a channel send to a value that is not a + // channel, or is a receive-only channel. + // + // Example: + // func f() { + // var x = 1 + // x <- "hello!" + // } + InvalidSend + + /* exprs > literal */ + + // DuplicateLitKey occurs when an index is duplicated in a slice, array, or + // map literal. + // + // Example: + // var _ = []int{0:1, 0:2} + // + // Example: + // var _ = map[string]int{"a": 1, "a": 2} + DuplicateLitKey + + // MissingLitKey occurs when a map literal is missing a key expression. + // + // Example: + // var _ = map[string]int{1} + MissingLitKey + + // InvalidLitIndex occurs when the key in a key-value element of a slice or + // array literal is not an integer constant. + // + // Example: + // var i = 0 + // var x = []string{i: "world"} + InvalidLitIndex + + // OversizeArrayLit occurs when an array literal exceeds its length. + // + // Example: + // var _ = [2]int{1,2,3} + OversizeArrayLit + + // MixedStructLit occurs when a struct literal contains a mix of positional + // and named elements. + // + // Example: + // var _ = struct{i, j int}{i: 1, 2} + MixedStructLit + + // InvalidStructLit occurs when a positional struct literal has an incorrect + // number of values. + // + // Example: + // var _ = struct{i, j int}{1,2,3} + InvalidStructLit + + // MissingLitField occurs when a struct literal refers to a field that does + // not exist on the struct type. + // + // Example: + // var _ = struct{i int}{j: 2} + MissingLitField + + // DuplicateLitField occurs when a struct literal contains duplicated + // fields. + // + // Example: + // var _ = struct{i int}{i: 1, i: 2} + DuplicateLitField + + // UnexportedLitField occurs when a positional struct literal implicitly + // assigns an unexported field of an imported type. + UnexportedLitField + + // InvalidLitField occurs when a field name is not a valid identifier. + // + // Example: + // var _ = struct{i int}{1: 1} + InvalidLitField + + // UntypedLit occurs when a composite literal omits a required type + // identifier. + // + // Example: + // type outer struct{ + // inner struct { i int } + // } + // + // var _ = outer{inner: {1}} + UntypedLit + + // InvalidLit occurs when a composite literal expression does not match its + // type. + // + // Example: + // type P *struct{ + // x int + // } + // var _ = P {} + InvalidLit + + /* exprs > selector */ + + // AmbiguousSelector occurs when a selector is ambiguous. + // + // Example: + // type E1 struct { i int } + // type E2 struct { i int } + // type T struct { E1; E2 } + // + // var x T + // var _ = x.i + AmbiguousSelector + + // UndeclaredImportedName occurs when a package-qualified identifier is + // undeclared by the imported package. + // + // Example: + // import "go/types" + // + // var _ = types.NotAnActualIdentifier + UndeclaredImportedName + + // UnexportedName occurs when a selector refers to an unexported identifier + // of an imported package. + // + // Example: + // import "reflect" + // + // type _ reflect.flag + UnexportedName + + // UndeclaredName occurs when an identifier is not declared in the current + // scope. + // + // Example: + // var x T + UndeclaredName + + // MissingFieldOrMethod occurs when a selector references a field or method + // that does not exist. + // + // Example: + // type T struct {} + // + // var x = T{}.f + MissingFieldOrMethod + + /* exprs > ... */ + + // BadDotDotDotSyntax occurs when a "..." occurs in a context where it is + // not valid. + // + // Example: + // var _ = map[int][...]int{0: {}} + BadDotDotDotSyntax + + // NonVariadicDotDotDot occurs when a "..." is used on the final argument to + // a non-variadic function. + // + // Example: + // func printArgs(s []string) { + // for _, a := range s { + // println(a) + // } + // } + // + // func f() { + // s := []string{"a", "b", "c"} + // printArgs(s...) + // } + NonVariadicDotDotDot + + // MisplacedDotDotDot occurs when a "..." is used somewhere other than the + // final argument to a function call. + // + // Example: + // func printArgs(args ...int) { + // for _, a := range args { + // println(a) + // } + // } + // + // func f() { + // a := []int{1,2,3} + // printArgs(0, a...) + // } + MisplacedDotDotDot + + // InvalidDotDotDotOperand occurs when a "..." operator is applied to a + // single-valued operand. + // + // Example: + // func printArgs(args ...int) { + // for _, a := range args { + // println(a) + // } + // } + // + // func f() { + // a := 1 + // printArgs(a...) + // } + // + // Example: + // func args() (int, int) { + // return 1, 2 + // } + // + // func printArgs(args ...int) { + // for _, a := range args { + // println(a) + // } + // } + // + // func g() { + // printArgs(args()...) + // } + InvalidDotDotDotOperand + + // InvalidDotDotDot occurs when a "..." is used in a non-variadic built-in + // function. + // + // Example: + // var s = []int{1, 2, 3} + // var l = len(s...) + InvalidDotDotDot + + /* exprs > built-in */ + + // UncalledBuiltin occurs when a built-in function is used as a + // function-valued expression, instead of being called. + // + // Per the spec: + // "The built-in functions do not have standard Go types, so they can only + // appear in call expressions; they cannot be used as function values." + // + // Example: + // var _ = copy + UncalledBuiltin + + // InvalidAppend occurs when append is called with a first argument that is + // not a slice. + // + // Example: + // var _ = append(1, 2) + InvalidAppend + + // InvalidCap occurs when an argument to the cap built-in function is not of + // supported type. + // + // See https://golang.org/ref/spec#Length_and_capacity for information on + // which underlying types are supported as arguments to cap and len. + // + // Example: + // var s = 2 + // var x = cap(s) + InvalidCap + + // InvalidClose occurs when close(...) is called with an argument that is + // not of channel type, or that is a receive-only channel. + // + // Example: + // func f() { + // var x int + // close(x) + // } + InvalidClose + + // InvalidCopy occurs when the arguments are not of slice type or do not + // have compatible type. + // + // See https://golang.org/ref/spec#Appending_and_copying_slices for more + // information on the type requirements for the copy built-in. + // + // Example: + // func f() { + // var x []int + // y := []int64{1,2,3} + // copy(x, y) + // } + InvalidCopy + + // InvalidComplex occurs when the complex built-in function is called with + // arguments with incompatible types. + // + // Example: + // var _ = complex(float32(1), float64(2)) + InvalidComplex + + // InvalidDelete occurs when the delete built-in function is called with a + // first argument that is not a map. + // + // Example: + // func f() { + // m := "hello" + // delete(m, "e") + // } + InvalidDelete + + // InvalidImag occurs when the imag built-in function is called with an + // argument that does not have complex type. + // + // Example: + // var _ = imag(int(1)) + InvalidImag + + // InvalidLen occurs when an argument to the len built-in function is not of + // supported type. + // + // See https://golang.org/ref/spec#Length_and_capacity for information on + // which underlying types are supported as arguments to cap and len. + // + // Example: + // var s = 2 + // var x = len(s) + InvalidLen + + // SwappedMakeArgs occurs when make is called with three arguments, and its + // length argument is larger than its capacity argument. + // + // Example: + // var x = make([]int, 3, 2) + SwappedMakeArgs + + // InvalidMake occurs when make is called with an unsupported type argument. + // + // See https://golang.org/ref/spec#Making_slices_maps_and_channels for + // information on the types that may be created using make. + // + // Example: + // var x = make(int) + InvalidMake + + // InvalidReal occurs when the real built-in function is called with an + // argument that does not have complex type. + // + // Example: + // var _ = real(int(1)) + InvalidReal + + /* exprs > assertion */ + + // InvalidAssert occurs when a type assertion is applied to a + // value that is not of interface type. + // + // Example: + // var x = 1 + // var _ = x.(float64) + InvalidAssert + + // ImpossibleAssert occurs for a type assertion x.(T) when the value x of + // interface cannot have dynamic type T, due to a missing or mismatching + // method on T. + // + // Example: + // type T int + // + // func (t *T) m() int { return int(*t) } + // + // type I interface { m() int } + // + // var x I + // var _ = x.(T) + ImpossibleAssert + + /* exprs > conversion */ + + // InvalidConversion occurs when the argument type cannot be converted to the + // target. + // + // See https://golang.org/ref/spec#Conversions for the rules of + // convertibility. + // + // Example: + // var x float64 + // var _ = string(x) + InvalidConversion + + // InvalidUntypedConversion occurs when there is no valid implicit + // conversion from an untyped value satisfying the type constraints of the + // context in which it is used. + // + // Example: + // var _ = 1 + "" + InvalidUntypedConversion + + /* offsetof */ + + // BadOffsetofSyntax occurs when unsafe.Offsetof is called with an argument + // that is not a selector expression. + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Offsetof(x) + BadOffsetofSyntax + + // InvalidOffsetof occurs when unsafe.Offsetof is called with a method + // selector, rather than a field selector, or when the field is embedded via + // a pointer. + // + // Per the spec: + // + // "If f is an embedded field, it must be reachable without pointer + // indirections through fields of the struct. " + // + // Example: + // import "unsafe" + // + // type T struct { f int } + // type S struct { *T } + // var s S + // var _ = unsafe.Offsetof(s.f) + // + // Example: + // import "unsafe" + // + // type S struct{} + // + // func (S) m() {} + // + // var s S + // var _ = unsafe.Offsetof(s.m) + InvalidOffsetof + + /* control flow > scope */ + + // UnusedExpr occurs when a side-effect free expression is used as a + // statement. Such a statement has no effect. + // + // Example: + // func f(i int) { + // i*i + // } + UnusedExpr + + // UnusedVar occurs when a variable is declared but unused. + // + // Example: + // func f() { + // x := 1 + // } + UnusedVar + + // MissingReturn occurs when a function with results is missing a return + // statement. + // + // Example: + // func f() int {} + MissingReturn + + // WrongResultCount occurs when a return statement returns an incorrect + // number of values. + // + // Example: + // func ReturnOne() int { + // return 1, 2 + // } + WrongResultCount + + // OutOfScopeResult occurs when the name of a value implicitly returned by + // an empty return statement is shadowed in a nested scope. + // + // Example: + // func factor(n int) (i int) { + // for i := 2; i < n; i++ { + // if n%i == 0 { + // return + // } + // } + // return 0 + // } + OutOfScopeResult + + /* control flow > if */ + + // InvalidCond occurs when an if condition is not a boolean expression. + // + // Example: + // func checkReturn(i int) { + // if i { + // panic("non-zero return") + // } + // } + InvalidCond + + /* control flow > for */ + + // InvalidPostDecl occurs when there is a declaration in a for-loop post + // statement. + // + // Example: + // func f() { + // for i := 0; i < 10; j := 0 {} + // } + InvalidPostDecl + + // InvalidChanRange occurs when a send-only channel used in a range + // expression. + // + // Example: + // func sum(c chan<- int) { + // s := 0 + // for i := range c { + // s += i + // } + // } + InvalidChanRange + + // InvalidIterVar occurs when two iteration variables are used while ranging + // over a channel. + // + // Example: + // func f(c chan int) { + // for k, v := range c { + // println(k, v) + // } + // } + InvalidIterVar + + // InvalidRangeExpr occurs when the type of a range expression is not array, + // slice, string, map, or channel. + // + // Example: + // func f(i int) { + // for j := range i { + // println(j) + // } + // } + InvalidRangeExpr + + /* control flow > switch */ + + // MisplacedBreak occurs when a break statement is not within a for, switch, + // or select statement of the innermost function definition. + // + // Example: + // func f() { + // break + // } + MisplacedBreak + + // MisplacedContinue occurs when a continue statement is not within a for + // loop of the innermost function definition. + // + // Example: + // func sumeven(n int) int { + // proceed := func() { + // continue + // } + // sum := 0 + // for i := 1; i <= n; i++ { + // if i % 2 != 0 { + // proceed() + // } + // sum += i + // } + // return sum + // } + MisplacedContinue + + // MisplacedFallthrough occurs when a fallthrough statement is not within an + // expression switch. + // + // Example: + // func typename(i interface{}) string { + // switch i.(type) { + // case int64: + // fallthrough + // case int: + // return "int" + // } + // return "unsupported" + // } + MisplacedFallthrough + + // DuplicateCase occurs when a type or expression switch has duplicate + // cases. + // + // Example: + // func printInt(i int) { + // switch i { + // case 1: + // println("one") + // case 1: + // println("One") + // } + // } + DuplicateCase + + // DuplicateDefault occurs when a type or expression switch has multiple + // default clauses. + // + // Example: + // func printInt(i int) { + // switch i { + // case 1: + // println("one") + // default: + // println("One") + // default: + // println("1") + // } + // } + DuplicateDefault + + // BadTypeKeyword occurs when a .(type) expression is used anywhere other + // than a type switch. + // + // Example: + // type I interface { + // m() + // } + // var t I + // var _ = t.(type) + BadTypeKeyword + + // InvalidTypeSwitch occurs when .(type) is used on an expression that is + // not of interface type. + // + // Example: + // func f(i int) { + // switch x := i.(type) {} + // } + InvalidTypeSwitch + + // InvalidExprSwitch occurs when a switch expression is not comparable. + // + // Example: + // func _() { + // var a struct{ _ func() } + // switch a /* ERROR cannot switch on a */ { + // } + // } + InvalidExprSwitch + + /* control flow > select */ + + // InvalidSelectCase occurs when a select case is not a channel send or + // receive. + // + // Example: + // func checkChan(c <-chan int) bool { + // select { + // case c: + // return true + // default: + // return false + // } + // } + InvalidSelectCase + + /* control flow > labels and jumps */ + + // UndeclaredLabel occurs when an undeclared label is jumped to. + // + // Example: + // func f() { + // goto L + // } + UndeclaredLabel + + // DuplicateLabel occurs when a label is declared more than once. + // + // Example: + // func f() int { + // L: + // L: + // return 1 + // } + DuplicateLabel + + // MisplacedLabel occurs when a break or continue label is not on a for, + // switch, or select statement. + // + // Example: + // func f() { + // L: + // a := []int{1,2,3} + // for _, e := range a { + // if e > 10 { + // break L + // } + // println(a) + // } + // } + MisplacedLabel + + // UnusedLabel occurs when a label is declared but not used. + // + // Example: + // func f() { + // L: + // } + UnusedLabel + + // JumpOverDecl occurs when a label jumps over a variable declaration. + // + // Example: + // func f() int { + // goto L + // x := 2 + // L: + // x++ + // return x + // } + JumpOverDecl + + // JumpIntoBlock occurs when a forward jump goes to a label inside a nested + // block. + // + // Example: + // func f(x int) { + // goto L + // if x > 0 { + // L: + // print("inside block") + // } + // } + JumpIntoBlock + + /* control flow > calls */ + + // InvalidMethodExpr occurs when a pointer method is called but the argument + // is not addressable. + // + // Example: + // type T struct {} + // + // func (*T) m() int { return 1 } + // + // var _ = T.m(T{}) + InvalidMethodExpr + + // WrongArgCount occurs when too few or too many arguments are passed by a + // function call. + // + // Example: + // func f(i int) {} + // var x = f() + WrongArgCount + + // InvalidCall occurs when an expression is called that is not of function + // type. + // + // Example: + // var x = "x" + // var y = x() + InvalidCall + + /* control flow > suspended */ + + // UnusedResults occurs when a restricted expression-only built-in function + // is suspended via go or defer. Such a suspension discards the results of + // these side-effect free built-in functions, and therefore is ineffectual. + // + // Example: + // func f(a []int) int { + // defer len(a) + // return i + // } + UnusedResults + + // InvalidDefer occurs when a deferred expression is not a function call, + // for example if the expression is a type conversion. + // + // Example: + // func f(i int) int { + // defer int32(i) + // return i + // } + InvalidDefer + + // InvalidGo occurs when a go expression is not a function call, for example + // if the expression is a type conversion. + // + // Example: + // func f(i int) int { + // go int32(i) + // return i + // } + InvalidGo + + // All codes below were added in Go 1.17. + + /* decl */ + + // BadDecl occurs when a declaration has invalid syntax. + BadDecl + + // RepeatedDecl occurs when an identifier occurs more than once on the left + // hand side of a short variable declaration. + // + // Example: + // func _() { + // x, y, y := 1, 2, 3 + // } + RepeatedDecl + + /* unsafe */ + + // InvalidUnsafeAdd occurs when unsafe.Add is called with a + // length argument that is not of integer type. + // + // Example: + // import "unsafe" + // + // var p unsafe.Pointer + // var _ = unsafe.Add(p, float64(1)) + InvalidUnsafeAdd + + // InvalidUnsafeSlice occurs when unsafe.Slice is called with a + // pointer argument that is not of pointer type or a length argument + // that is not of integer type, negative, or out of bounds. + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(x, 1) + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(&x, float64(1)) + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(&x, -1) + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.Slice(&x, uint64(1) << 63) + InvalidUnsafeSlice + + // All codes below were added in Go 1.18. + + /* features */ + + // UnsupportedFeature occurs when a language feature is used that is not + // supported at this Go version. + UnsupportedFeature + + /* type params */ + + // NotAGenericType occurs when a non-generic type is used where a generic + // type is expected: in type or function instantiation. + // + // Example: + // type T int + // + // var _ T[int] + NotAGenericType + + // WrongTypeArgCount occurs when a type or function is instantiated with an + // incorrect number of type arguments, including when a generic type or + // function is used without instantiation. + // + // Errors involving failed type inference are assigned other error codes. + // + // Example: + // type T[p any] int + // + // var _ T[int, string] + // + // Example: + // func f[T any]() {} + // + // var x = f + WrongTypeArgCount + + // CannotInferTypeArgs occurs when type or function type argument inference + // fails to infer all type arguments. + // + // Example: + // func f[T any]() {} + // + // func _() { + // f() + // } + // + // Example: + // type N[P, Q any] struct{} + // + // var _ N[int] + CannotInferTypeArgs + + // InvalidTypeArg occurs when a type argument does not satisfy its + // corresponding type parameter constraints. + // + // Example: + // type T[P ~int] struct{} + // + // var _ T[string] + InvalidTypeArg // arguments? InferenceFailed + + // InvalidInstanceCycle occurs when an invalid cycle is detected + // within the instantiation graph. + // + // Example: + // func f[T any]() { f[*T]() } + InvalidInstanceCycle + + // InvalidUnion occurs when an embedded union or approximation element is + // not valid. + // + // Example: + // type _ interface { + // ~int | interface{ m() } + // } + InvalidUnion + + // MisplacedConstraintIface occurs when a constraint-type interface is used + // outside of constraint position. + // + // Example: + // type I interface { ~int } + // + // var _ I + MisplacedConstraintIface + + // InvalidMethodTypeParams occurs when methods have type parameters. + // + // It cannot be encountered with an AST parsed using go/parser. + InvalidMethodTypeParams + + // MisplacedTypeParam occurs when a type parameter is used in a place where + // it is not permitted. + // + // Example: + // type T[P any] P + // + // Example: + // type T[P any] struct{ *P } + MisplacedTypeParam + + // InvalidUnsafeSliceData occurs when unsafe.SliceData is called with + // an argument that is not of slice type. It also occurs if it is used + // in a package compiled for a language version before go1.20. + // + // Example: + // import "unsafe" + // + // var x int + // var _ = unsafe.SliceData(x) + InvalidUnsafeSliceData + + // InvalidUnsafeString occurs when unsafe.String is called with + // a length argument that is not of integer type, negative, or + // out of bounds. It also occurs if it is used in a package + // compiled for a language version before go1.20. + // + // Example: + // import "unsafe" + // + // var b [10]byte + // var _ = unsafe.String(&b[0], -1) + InvalidUnsafeString + + // InvalidUnsafeStringData occurs if it is used in a package + // compiled for a language version before go1.20. + _ // not used anymore + +) diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/errorcode_string.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/errorcode_string.go new file mode 100644 index 00000000..15ecf7c5 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/errorcode_string.go @@ -0,0 +1,179 @@ +// Code generated by "stringer -type=ErrorCode"; DO NOT EDIT. + +package typesinternal + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidSyntaxTree - -1] + _ = x[Test-1] + _ = x[BlankPkgName-2] + _ = x[MismatchedPkgName-3] + _ = x[InvalidPkgUse-4] + _ = x[BadImportPath-5] + _ = x[BrokenImport-6] + _ = x[ImportCRenamed-7] + _ = x[UnusedImport-8] + _ = x[InvalidInitCycle-9] + _ = x[DuplicateDecl-10] + _ = x[InvalidDeclCycle-11] + _ = x[InvalidTypeCycle-12] + _ = x[InvalidConstInit-13] + _ = x[InvalidConstVal-14] + _ = x[InvalidConstType-15] + _ = x[UntypedNilUse-16] + _ = x[WrongAssignCount-17] + _ = x[UnassignableOperand-18] + _ = x[NoNewVar-19] + _ = x[MultiValAssignOp-20] + _ = x[InvalidIfaceAssign-21] + _ = x[InvalidChanAssign-22] + _ = x[IncompatibleAssign-23] + _ = x[UnaddressableFieldAssign-24] + _ = x[NotAType-25] + _ = x[InvalidArrayLen-26] + _ = x[BlankIfaceMethod-27] + _ = x[IncomparableMapKey-28] + _ = x[InvalidIfaceEmbed-29] + _ = x[InvalidPtrEmbed-30] + _ = x[BadRecv-31] + _ = x[InvalidRecv-32] + _ = x[DuplicateFieldAndMethod-33] + _ = x[DuplicateMethod-34] + _ = x[InvalidBlank-35] + _ = x[InvalidIota-36] + _ = x[MissingInitBody-37] + _ = x[InvalidInitSig-38] + _ = x[InvalidInitDecl-39] + _ = x[InvalidMainDecl-40] + _ = x[TooManyValues-41] + _ = x[NotAnExpr-42] + _ = x[TruncatedFloat-43] + _ = x[NumericOverflow-44] + _ = x[UndefinedOp-45] + _ = x[MismatchedTypes-46] + _ = x[DivByZero-47] + _ = x[NonNumericIncDec-48] + _ = x[UnaddressableOperand-49] + _ = x[InvalidIndirection-50] + _ = x[NonIndexableOperand-51] + _ = x[InvalidIndex-52] + _ = x[SwappedSliceIndices-53] + _ = x[NonSliceableOperand-54] + _ = x[InvalidSliceExpr-55] + _ = x[InvalidShiftCount-56] + _ = x[InvalidShiftOperand-57] + _ = x[InvalidReceive-58] + _ = x[InvalidSend-59] + _ = x[DuplicateLitKey-60] + _ = x[MissingLitKey-61] + _ = x[InvalidLitIndex-62] + _ = x[OversizeArrayLit-63] + _ = x[MixedStructLit-64] + _ = x[InvalidStructLit-65] + _ = x[MissingLitField-66] + _ = x[DuplicateLitField-67] + _ = x[UnexportedLitField-68] + _ = x[InvalidLitField-69] + _ = x[UntypedLit-70] + _ = x[InvalidLit-71] + _ = x[AmbiguousSelector-72] + _ = x[UndeclaredImportedName-73] + _ = x[UnexportedName-74] + _ = x[UndeclaredName-75] + _ = x[MissingFieldOrMethod-76] + _ = x[BadDotDotDotSyntax-77] + _ = x[NonVariadicDotDotDot-78] + _ = x[MisplacedDotDotDot-79] + _ = x[InvalidDotDotDotOperand-80] + _ = x[InvalidDotDotDot-81] + _ = x[UncalledBuiltin-82] + _ = x[InvalidAppend-83] + _ = x[InvalidCap-84] + _ = x[InvalidClose-85] + _ = x[InvalidCopy-86] + _ = x[InvalidComplex-87] + _ = x[InvalidDelete-88] + _ = x[InvalidImag-89] + _ = x[InvalidLen-90] + _ = x[SwappedMakeArgs-91] + _ = x[InvalidMake-92] + _ = x[InvalidReal-93] + _ = x[InvalidAssert-94] + _ = x[ImpossibleAssert-95] + _ = x[InvalidConversion-96] + _ = x[InvalidUntypedConversion-97] + _ = x[BadOffsetofSyntax-98] + _ = x[InvalidOffsetof-99] + _ = x[UnusedExpr-100] + _ = x[UnusedVar-101] + _ = x[MissingReturn-102] + _ = x[WrongResultCount-103] + _ = x[OutOfScopeResult-104] + _ = x[InvalidCond-105] + _ = x[InvalidPostDecl-106] + _ = x[InvalidChanRange-107] + _ = x[InvalidIterVar-108] + _ = x[InvalidRangeExpr-109] + _ = x[MisplacedBreak-110] + _ = x[MisplacedContinue-111] + _ = x[MisplacedFallthrough-112] + _ = x[DuplicateCase-113] + _ = x[DuplicateDefault-114] + _ = x[BadTypeKeyword-115] + _ = x[InvalidTypeSwitch-116] + _ = x[InvalidExprSwitch-117] + _ = x[InvalidSelectCase-118] + _ = x[UndeclaredLabel-119] + _ = x[DuplicateLabel-120] + _ = x[MisplacedLabel-121] + _ = x[UnusedLabel-122] + _ = x[JumpOverDecl-123] + _ = x[JumpIntoBlock-124] + _ = x[InvalidMethodExpr-125] + _ = x[WrongArgCount-126] + _ = x[InvalidCall-127] + _ = x[UnusedResults-128] + _ = x[InvalidDefer-129] + _ = x[InvalidGo-130] + _ = x[BadDecl-131] + _ = x[RepeatedDecl-132] + _ = x[InvalidUnsafeAdd-133] + _ = x[InvalidUnsafeSlice-134] + _ = x[UnsupportedFeature-135] + _ = x[NotAGenericType-136] + _ = x[WrongTypeArgCount-137] + _ = x[CannotInferTypeArgs-138] + _ = x[InvalidTypeArg-139] + _ = x[InvalidInstanceCycle-140] + _ = x[InvalidUnion-141] + _ = x[MisplacedConstraintIface-142] + _ = x[InvalidMethodTypeParams-143] + _ = x[MisplacedTypeParam-144] + _ = x[InvalidUnsafeSliceData-145] + _ = x[InvalidUnsafeString-146] +} + +const ( + _ErrorCode_name_0 = "InvalidSyntaxTree" + _ErrorCode_name_1 = "TestBlankPkgNameMismatchedPkgNameInvalidPkgUseBadImportPathBrokenImportImportCRenamedUnusedImportInvalidInitCycleDuplicateDeclInvalidDeclCycleInvalidTypeCycleInvalidConstInitInvalidConstValInvalidConstTypeUntypedNilUseWrongAssignCountUnassignableOperandNoNewVarMultiValAssignOpInvalidIfaceAssignInvalidChanAssignIncompatibleAssignUnaddressableFieldAssignNotATypeInvalidArrayLenBlankIfaceMethodIncomparableMapKeyInvalidIfaceEmbedInvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDotInvalidDotDotDotOperandInvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDeclInvalidChanRangeInvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString" +) + +var ( + _ErrorCode_index_1 = [...]uint16{0, 4, 16, 33, 46, 59, 71, 85, 97, 113, 126, 142, 158, 174, 189, 205, 218, 234, 253, 261, 277, 295, 312, 330, 354, 362, 377, 393, 411, 428, 443, 450, 461, 484, 499, 511, 522, 537, 551, 566, 581, 594, 603, 617, 632, 643, 658, 667, 683, 703, 721, 740, 752, 771, 790, 806, 823, 842, 856, 867, 882, 895, 910, 926, 940, 956, 971, 988, 1006, 1021, 1031, 1041, 1058, 1080, 1094, 1108, 1128, 1146, 1166, 1184, 1207, 1223, 1238, 1251, 1261, 1273, 1284, 1298, 1311, 1322, 1332, 1347, 1358, 1369, 1382, 1398, 1415, 1439, 1456, 1471, 1481, 1490, 1503, 1519, 1535, 1546, 1561, 1577, 1591, 1607, 1621, 1638, 1658, 1671, 1687, 1701, 1718, 1735, 1752, 1767, 1781, 1795, 1806, 1818, 1831, 1848, 1861, 1872, 1885, 1897, 1906, 1913, 1925, 1941, 1959, 1977, 1992, 2009, 2028, 2042, 2062, 2074, 2098, 2121, 2139, 2161, 2180} +) + +func (i ErrorCode) String() string { + switch { + case i == -1: + return _ErrorCode_name_0 + case 1 <= i && i <= 146: + i -= 1 + return _ErrorCode_name_1[_ErrorCode_index_1[i]:_ErrorCode_index_1[i+1]] + default: + return "ErrorCode(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/fx.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/fx.go new file mode 100644 index 00000000..c846a53d --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/fx.go @@ -0,0 +1,88 @@ +// Copyright 2025 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 typesinternal + +import ( + "go/ast" + "go/token" + "go/types" +) + +// NoEffects reports whether the expression has no side effects, i.e., it +// does not modify the memory state. This function is conservative: it may +// return false even when the expression has no effect. +func NoEffects(info *types.Info, expr ast.Expr) bool { + noEffects := true + ast.Inspect(expr, func(n ast.Node) bool { + switch v := n.(type) { + case nil, *ast.Ident, *ast.BasicLit, *ast.BinaryExpr, *ast.ParenExpr, + *ast.SelectorExpr, *ast.IndexExpr, *ast.SliceExpr, *ast.TypeAssertExpr, + *ast.StarExpr, *ast.CompositeLit, + // non-expressions that may appear within expressions + *ast.KeyValueExpr, + *ast.FieldList, + *ast.Field, + *ast.Ellipsis, + *ast.IndexListExpr: + // No effect. + + case *ast.ArrayType, + *ast.StructType, + *ast.ChanType, + *ast.FuncType, + *ast.MapType, + *ast.InterfaceType: + // Type syntax: no effects, recursively. + // Prune descent. + return false + + case *ast.UnaryExpr: + // Channel send <-ch has effects. + if v.Op == token.ARROW { + noEffects = false + } + + case *ast.CallExpr: + // Type conversion has no effects. + if !info.Types[v.Fun].IsType() { + if CallsPureBuiltin(info, v) { + // A call such as len(e) has no effects of its + // own, though the subexpression e might. + } else { + noEffects = false + } + } + + case *ast.FuncLit: + // A FuncLit has no effects, but do not descend into it. + return false + + default: + // All other expressions have effects + noEffects = false + } + + return noEffects + }) + return noEffects +} + +// CallsPureBuiltin reports whether call is a call of a built-in +// function that is a pure computation over its operands (analogous to +// a + operator). Because it does not depend on program state, it may +// be evaluated at any point--though not necessarily at multiple +// points (consider new, make). +func CallsPureBuiltin(info *types.Info, call *ast.CallExpr) bool { + if id, ok := ast.Unparen(call.Fun).(*ast.Ident); ok { + if b, ok := info.ObjectOf(id).(*types.Builtin); ok { + switch b.Name() { + case "len", "cap", "complex", "imag", "real", "make", "new", "max", "min": + return true + } + // Not: append clear close copy delete panic print println recover + } + } + return false +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/isnamed.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/isnamed.go new file mode 100644 index 00000000..e0d63c46 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/isnamed.go @@ -0,0 +1,71 @@ +// Copyright 2025 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 typesinternal + +import ( + "go/types" + "slices" +) + +// IsTypeNamed reports whether t is (or is an alias for) a +// package-level defined type with the given package path and one of +// the given names. It returns false if t is nil. +// +// This function avoids allocating the concatenation of "pkg.Name", +// which is important for the performance of syntax matching. +func IsTypeNamed(t types.Type, pkgPath string, names ...string) bool { + if named, ok := types.Unalias(t).(*types.Named); ok { + tname := named.Obj() + return tname != nil && + IsPackageLevel(tname) && + tname.Pkg().Path() == pkgPath && + slices.Contains(names, tname.Name()) + } + return false +} + +// IsPointerToNamed reports whether t is (or is an alias for) a pointer to a +// package-level defined type with the given package path and one of the given +// names. It returns false if t is not a pointer type. +func IsPointerToNamed(t types.Type, pkgPath string, names ...string) bool { + r := Unpointer(t) + if r == t { + return false + } + return IsTypeNamed(r, pkgPath, names...) +} + +// IsFunctionNamed reports whether obj is a package-level function +// defined in the given package and has one of the given names. +// It returns false if obj is nil. +// +// This function avoids allocating the concatenation of "pkg.Name", +// which is important for the performance of syntax matching. +func IsFunctionNamed(obj types.Object, pkgPath string, names ...string) bool { + f, ok := obj.(*types.Func) + return ok && + IsPackageLevel(obj) && + f.Pkg().Path() == pkgPath && + f.Signature().Recv() == nil && + slices.Contains(names, f.Name()) +} + +// IsMethodNamed reports whether obj is a method defined on a +// package-level type with the given package and type name, and has +// one of the given names. It returns false if obj is nil. +// +// This function avoids allocating the concatenation of "pkg.TypeName.Name", +// which is important for the performance of syntax matching. +func IsMethodNamed(obj types.Object, pkgPath string, typeName string, names ...string) bool { + if fn, ok := obj.(*types.Func); ok { + if recv := fn.Signature().Recv(); recv != nil { + _, T := ReceiverNamed(recv) + return T != nil && + IsTypeNamed(T, pkgPath, typeName) && + slices.Contains(names, fn.Name()) + } + } + return false +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/qualifier.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/qualifier.go new file mode 100644 index 00000000..4e2756fc --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/qualifier.go @@ -0,0 +1,54 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typesinternal + +import ( + "go/ast" + "go/types" + "strconv" +) + +// FileQualifier returns a [types.Qualifier] function that qualifies +// imported symbols appropriately based on the import environment of a given +// file. +// If the same package is imported multiple times, the last appearance is +// recorded. +// +// TODO(adonovan): this function ignores the effect of shadowing. It +// should accept a [token.Pos] and a [types.Info] and compute only the +// set of imports that are not shadowed at that point, analogous to +// [analysis.AddImport]. It could also compute (as a side +// effect) the set of additional imports required to ensure that there +// is an accessible import for each necessary package, making it +// converge even more closely with AddImport. +func FileQualifier(f *ast.File, pkg *types.Package) types.Qualifier { + // Construct mapping of import paths to their defined names. + // It is only necessary to look at renaming imports. + imports := make(map[string]string) + for _, imp := range f.Imports { + if imp.Name != nil && imp.Name.Name != "_" { + path, _ := strconv.Unquote(imp.Path.Value) + imports[path] = imp.Name.Name + } + } + + // Define qualifier to replace full package paths with names of the imports. + return func(p *types.Package) string { + if p == nil || p == pkg { + return "" + } + + if name, ok := imports[p.Path()]; ok { + if name == "." { + return "" + } else { + return name + } + } + + // If there is no local renaming, fall back to the package name. + return p.Name() + } +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/recv.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/recv.go new file mode 100644 index 00000000..8352ea76 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/recv.go @@ -0,0 +1,44 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typesinternal + +import ( + "go/types" +) + +// ReceiverNamed returns the named type (if any) associated with the +// type of recv, which may be of the form N or *N, or aliases thereof. +// It also reports whether a Pointer was present. +// +// The named result may be nil if recv is from a method on an +// anonymous interface or struct types or in ill-typed code. +func ReceiverNamed(recv *types.Var) (isPtr bool, named *types.Named) { + t := recv.Type() + if ptr, ok := types.Unalias(t).(*types.Pointer); ok { + isPtr = true + t = ptr.Elem() + } + named, _ = types.Unalias(t).(*types.Named) + return +} + +// Unpointer returns T given *T or an alias thereof. +// For all other types it is the identity function. +// It does not look at underlying types. +// The result may be an alias. +// +// Use this function to strip off the optional pointer on a receiver +// in a field or method selection, without losing the named type +// (which is needed to compute the method set). +// +// See also [typeparams.MustDeref], which removes one level of +// indirection from the type, regardless of named types (analogous to +// a LOAD instruction). +func Unpointer(t types.Type) types.Type { + if ptr, ok := types.Unalias(t).(*types.Pointer); ok { + return ptr.Elem() + } + return t +} diff --git a/vendor/honnef.co/go/tools/internal/typesinternal/typeindex/typeindex.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/typeindex/typeindex.go similarity index 74% rename from vendor/honnef.co/go/tools/internal/typesinternal/typeindex/typeindex.go rename to vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/typeindex/typeindex.go index fccc2ba7..9a8ac36e 100644 --- a/vendor/honnef.co/go/tools/internal/typesinternal/typeindex/typeindex.go +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/typeindex/typeindex.go @@ -17,13 +17,10 @@ import ( "golang.org/x/tools/go/ast/edge" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "honnef.co/go/tools/internal/xtools-internal/astutil" + "honnef.co/go/tools/internal/xtools-internal/typesinternal" ) -// IsPackageLevel reports whether obj is a package-level symbol. -func IsPackageLevel(obj types.Object) bool { - return obj.Pkg() != nil && obj.Parent() == obj.Pkg().Scope() -} - // New constructs an Index for the package of type-annotated syntax // // TODO(adonovan): accept a FileSet too? @@ -59,28 +56,63 @@ func New(inspect *inspector.Inspector, pkg *types.Package, info *types.Info) *In if obj := info.Uses[n]; obj != nil { // Index indirect dependencies (via fields and methods). - if !IsPackageLevel(obj) { + if !typesinternal.IsPackageLevel(obj) { addPackage(obj.Pkg()) } - us, ok := ix.uses[obj] - if !ok { - us = &uses{} - us.code = us.initial[:0] - ix.uses[obj] = us - } - delta := cur.Index() - us.last - if delta < 0 { - panic("non-monotonic") + for { + us, ok := ix.uses[obj] + if !ok { + us = &uses{} + us.code = us.initial[:0] + ix.uses[obj] = us + } + delta := cur.Index() - us.last + if delta < 0 { + panic("non-monotonic") + } + us.code = binary.AppendUvarint(us.code, uint64(delta)) + us.last = cur.Index() + + // If n is a selection of a field or method of an instantiated + // type, also record a use of the generic field or method. + obj, ok = objectOrigin(obj) + if !ok { + break + } } - us.code = binary.AppendUvarint(us.code, uint64(delta)) - us.last = cur.Index() } } } return ix } +// objectOrigin returns the generic object for obj if it is a field or +// method of an instantied type; zero otherwise. +// +// (This operation is appropriate only for selections. +// Lexically resolved references always resolve to the generic. +// Although Named and Alias types also use Origin to express +// an instance/generic distinction, that's in the domain +// of Types; their TypeName objects always refer to the generic.) +func objectOrigin(obj types.Object) (types.Object, bool) { + var origin types.Object + switch obj := obj.(type) { + case *types.Func: + if obj.Signature().Recv() != nil { + origin = obj.Origin() // G[int].method -> G[T].method + } + case *types.Var: + if obj.IsField() { + origin = obj.Origin() // G[int].field -> G[T].field + } + } + if origin != nil && origin != obj { + return origin, true + } + return nil, false +} + // An Index holds an index mapping [types.Object] symbols to their syntax. // In effect, it is the inverse of [types.Info]. type Index struct { @@ -110,6 +142,10 @@ type uses struct { // Uses returns the sequence of Cursors of [*ast.Ident]s in this package // that refer to obj. If obj is nil, the sequence is empty. +// +// Uses, unlike the Uses field of [types.Info], records additional +// entries mapping fields and methods of generic types to references +// through their corresponding instantiated objects. func (ix *Index) Uses(obj types.Object) iter.Seq[inspector.Cursor] { return func(yield func(inspector.Cursor) bool) { if uses := ix.uses[obj]; uses != nil { @@ -182,10 +218,9 @@ func (ix *Index) Selection(path, typename, name string) types.Object { func (ix *Index) Calls(callee types.Object) iter.Seq[inspector.Cursor] { return func(yield func(inspector.Cursor) bool) { for cur := range ix.Uses(callee) { - ek, _ := cur.ParentEdge() - // The call may be of the form f() or x.f(), // optionally with parens; ascend from f to call. + // See logic in [typesinternal.UsedIdent], to which this is dual. // // It is tempting but wrong to use the first // CallExpr ancestor: we have to make sure the @@ -194,25 +229,20 @@ func (ix *Index) Calls(callee types.Object) iter.Seq[inspector.Cursor] { // Avoiding Enclosing is also significantly faster. // inverse unparen: f -> (f) - for ek == edge.ParenExpr_X { - cur = cur.Parent() - ek, _ = cur.ParentEdge() - } + cur = astutil.UnparenEnclosingCursor(cur) - // ascend selector: f -> x.f - if ek == edge.SelectorExpr_Sel { - cur = cur.Parent() - ek, _ = cur.ParentEdge() + // ascend selector (or qualified identifier): f -> x.f + if cur.ParentEdgeKind() == edge.SelectorExpr_Sel { + cur = astutil.UnparenEnclosingCursor(cur.Parent()) } - // inverse unparen again - for ek == edge.ParenExpr_X { - cur = cur.Parent() - ek, _ = cur.ParentEdge() + // ascend typeparams: f -> f[T]; f -> f[T1, T2] + if ek := cur.ParentEdgeKind(); ek == edge.IndexExpr_X || ek == edge.IndexListExpr_X { + cur = astutil.UnparenEnclosingCursor(cur.Parent()) } // ascend from f or x.f to call - if ek == edge.CallExpr_Fun { + if cur.ParentEdgeKind() == edge.CallExpr_Fun { curCall := cur.Parent() call := curCall.Node().(*ast.CallExpr) if typeutil.Callee(ix.info, call) == callee { diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/types.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/types.go new file mode 100644 index 00000000..50942b77 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/types.go @@ -0,0 +1,272 @@ +// 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 typesinternal provides helpful operators for dealing with +// go/types: +// +// - operators for querying typed syntax trees (e.g. [Imports], [IsFunctionNamed]); +// - functions for converting types to strings or syntax (e.g. [TypeExpr], FileQualifier]); +// - helpers for working with the [go/types] API (e.g. [NewTypesInfo]); +// - access to internal go/types APIs that are not yet +// exported (e.g. [SetUsesCgo], [ErrorCodeStartEnd], [VarKind]); and +// - common algorithms related to types (e.g. [TooNewStdSymbols]). +// +// See also: +// - [honnef.co/go/tools/internal/xtools-internal/astutil], for operations on untyped syntax; +// - [honnef.co/go/tools/internal/xtools-internal/analysisinernal], for helpers for analyzers; +// - [honnef.co/go/tools/internal/xtools-internal/refactor], for operators to compute text edits. +package typesinternal + +import ( + "go/ast" + "go/token" + "go/types" + "iter" + "reflect" + + "golang.org/x/tools/go/ast/inspector" +) + +func SetUsesCgo(conf *types.Config) bool { + v := reflect.ValueOf(conf).Elem() + + f := v.FieldByName("go115UsesCgo") + if !f.IsValid() { + f = v.FieldByName("UsesCgo") + if !f.IsValid() { + return false + } + } + + *(*bool)(f.Addr().UnsafePointer()) = true + + return true +} + +// ErrorCodeStartEnd extracts additional information from types.Error values +// generated by Go version 1.16 and later: the error code, start position, and +// end position. If all positions are valid, start <= err.Pos <= end. +// +// If the data could not be read, the final result parameter will be false. +// +// TODO(adonovan): eliminate start/end when proposal #71803 is accepted. +func ErrorCodeStartEnd(err types.Error) (code ErrorCode, start, end token.Pos, ok bool) { + var data [3]int + // By coincidence all of these fields are ints, which simplifies things. + v := reflect.ValueOf(err) + for i, name := range []string{"go116code", "go116start", "go116end"} { + f := v.FieldByName(name) + if !f.IsValid() { + return 0, 0, 0, false + } + data[i] = int(f.Int()) + } + return ErrorCode(data[0]), token.Pos(data[1]), token.Pos(data[2]), true +} + +// NameRelativeTo returns a types.Qualifier that qualifies members of +// all packages other than pkg, using only the package name. +// (By contrast, [types.RelativeTo] uses the complete package path, +// which is often excessive.) +// +// If pkg is nil, it is equivalent to [*types.Package.Name]. +// +// TODO(adonovan): all uses of this with TypeString should be +// eliminated when https://go.dev/issues/75604 is resolved. +func NameRelativeTo(pkg *types.Package) types.Qualifier { + return func(other *types.Package) string { + if pkg != nil && pkg == other { + return "" // same package; unqualified + } + return other.Name() + } +} + +// TypeNameFor returns the type name symbol for the specified type, if +// it is a [*types.Alias], [*types.Named], [*types.TypeParam], or a +// [*types.Basic] representing a type. +// +// For all other types, and for Basic types representing a builtin, +// constant, or nil, it returns nil. Be careful not to convert the +// resulting nil pointer to a [types.Object]! +// +// If t is the type of a constant, it may be an "untyped" type, which +// has no TypeName. To access the name of such types (e.g. "untyped +// int"), use [types.Basic.Name]. +func TypeNameFor(t types.Type) *types.TypeName { + switch t := t.(type) { + case *types.Alias: + return t.Obj() + case *types.Named: + return t.Obj() + case *types.TypeParam: + return t.Obj() + case *types.Basic: + // See issues #71886 and #66890 for some history. + if tname, ok := types.Universe.Lookup(t.Name()).(*types.TypeName); ok { + return tname + } + } + return nil +} + +// A NamedOrAlias is a [types.Type] that is named (as +// defined by the spec) and capable of bearing type parameters: it +// abstracts aliases ([types.Alias]) and defined types +// ([types.Named]). +// +// Every type declared by an explicit "type" declaration is a +// NamedOrAlias. (Built-in type symbols may additionally +// have type [types.Basic], which is not a NamedOrAlias, +// though the spec regards them as "named"; see [TypeNameFor].) +// +// NamedOrAlias cannot expose the Origin method, because +// [types.Alias.Origin] and [types.Named.Origin] have different +// (covariant) result types; use [Origin] instead. +type NamedOrAlias interface { + types.Type + Obj() *types.TypeName + TypeArgs() *types.TypeList + TypeParams() *types.TypeParamList + SetTypeParams(tparams []*types.TypeParam) +} + +var ( + _ NamedOrAlias = (*types.Alias)(nil) + _ NamedOrAlias = (*types.Named)(nil) +) + +// Origin returns the generic type of the Named or Alias type t if it +// is instantiated, otherwise it returns t. +func Origin(t NamedOrAlias) NamedOrAlias { + switch t := t.(type) { + case *types.Alias: + return t.Origin() + case *types.Named: + return t.Origin() + } + return t +} + +// IsPackageLevel reports whether obj is a package-level symbol. +func IsPackageLevel(obj types.Object) bool { + return obj.Pkg() != nil && obj.Parent() == obj.Pkg().Scope() +} + +// NewTypesInfo returns a *types.Info with all maps populated. +func NewTypesInfo() *types.Info { + return &types.Info{ + Types: map[ast.Expr]types.TypeAndValue{}, + Instances: map[*ast.Ident]types.Instance{}, + Defs: map[*ast.Ident]types.Object{}, + Uses: map[*ast.Ident]types.Object{}, + Implicits: map[ast.Node]types.Object{}, + Selections: map[*ast.SelectorExpr]*types.Selection{}, + Scopes: map[ast.Node]*types.Scope{}, + FileVersions: map[*ast.File]string{}, + } +} + +// EnclosingScope returns the innermost block logically enclosing the cursor. +func EnclosingScope(info *types.Info, cur inspector.Cursor) *types.Scope { + for cur := range cur.Enclosing() { + n := cur.Node() + // A function's Scope is associated with its FuncType. + switch f := n.(type) { + case *ast.FuncDecl: + n = f.Type + case *ast.FuncLit: + n = f.Type + } + if b := info.Scopes[n]; b != nil { + return b + } + } + panic("no Scope for *ast.File") +} + +// Imports reports whether path is imported by pkg. +func Imports(pkg *types.Package, path string) bool { + for _, imp := range pkg.Imports() { + if imp.Path() == path { + return true + } + } + return false +} + +// ObjectKind returns a description of the object's kind. +// +// from objectKind in go/types +func ObjectKind(obj types.Object) string { + switch obj := obj.(type) { + case *types.PkgName: + return "package name" + case *types.Const: + return "constant" + case *types.TypeName: + if obj.IsAlias() { + return "type alias" + } else if _, ok := obj.Type().(*types.TypeParam); ok { + return "type parameter" + } else { + return "defined type" + } + case *types.Var: + switch obj.Kind() { + case PackageVar: + return "package-level variable" + case LocalVar: + return "local variable" + case RecvVar: + return "receiver" + case ParamVar: + return "parameter" + case ResultVar: + return "result variable" + case FieldVar: + return "struct field" + } + case *types.Func: + if obj.Signature().Recv() != nil { + return "method" + } else { + return "function" + } + case *types.Label: + return "label" + case *types.Builtin: + return "built-in function" + case *types.Nil: + return "untyped nil" + } + return "unknown symbol" +} + +// ImplicitFieldSelections returns the sequence of implicit embedded fields +// traversed by the given selection. It skips the final leaf field or method. +// The boolean component indicates whether the traversal traversed a pointer. +func ImplicitFieldSelections(seln types.Selection) iter.Seq2[*types.Var, bool] { + return func(yield func(*types.Var, bool) bool) { + var ( + t = seln.Recv() + indices = seln.Index() + ) + for _, idx := range indices[:len(indices)-1] { + ptr, isPtr := t.Underlying().(*types.Pointer) + if isPtr { + t = ptr.Elem() + } + structType, ok := t.Underlying().(*types.Struct) + if !ok { + break + } + field := structType.Field(idx) + if !yield(field, isPtr) { + break + } + t = field.Type() + } + } +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/varkind.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/varkind.go new file mode 100644 index 00000000..26499cdd --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/varkind.go @@ -0,0 +1,23 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.25 + +package typesinternal + +import "go/types" + +type VarKind = types.VarKind + +const ( + PackageVar = types.PackageVar + LocalVar = types.LocalVar + RecvVar = types.RecvVar + ParamVar = types.ParamVar + ResultVar = types.ResultVar + FieldVar = types.FieldVar +) + +func GetVarKind(v *types.Var) VarKind { return v.Kind() } +func SetVarKind(v *types.Var, kind VarKind) { v.SetKind(kind) } diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/varkind_go124.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/varkind_go124.go new file mode 100644 index 00000000..17b1804b --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/varkind_go124.go @@ -0,0 +1,39 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.25 + +package typesinternal + +import "go/types" + +type VarKind uint8 + +const ( + _ VarKind = iota // (not meaningful) + PackageVar // a package-level variable + LocalVar // a local variable + RecvVar // a method receiver variable + ParamVar // a function parameter variable + ResultVar // a function result variable + FieldVar // a struct field +) + +func (kind VarKind) String() string { + return [...]string{ + 0: "VarKind(0)", + PackageVar: "PackageVar", + LocalVar: "LocalVar", + RecvVar: "RecvVar", + ParamVar: "ParamVar", + ResultVar: "ResultVar", + FieldVar: "FieldVar", + }[kind] +} + +// GetVarKind returns an invalid VarKind. +func GetVarKind(v *types.Var) VarKind { return 0 } + +// SetVarKind has no effect. +func SetVarKind(v *types.Var, kind VarKind) {} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/zerovalue.go b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/zerovalue.go new file mode 100644 index 00000000..d612a710 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/typesinternal/zerovalue.go @@ -0,0 +1,381 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typesinternal + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "strings" +) + +// ZeroString returns the string representation of the zero value for any type t. +// The boolean result indicates whether the type is or contains an invalid type +// or a non-basic (constraint) interface type. +// +// Even for invalid input types, ZeroString may return a partially correct +// string representation. The caller should use the returned isValid boolean +// to determine the validity of the expression. +// +// When assigning to a wider type (such as 'any'), it's the caller's +// responsibility to handle any necessary type conversions. +// +// This string can be used on the right-hand side of an assignment where the +// left-hand side has that explicit type. +// References to named types are qualified by an appropriate (optional) +// qualifier function. +// Exception: This does not apply to tuples. Their string representation is +// informational only and cannot be used in an assignment. +// +// See [ZeroExpr] for a variant that returns an [ast.Expr]. +func ZeroString(t types.Type, qual types.Qualifier) (_ string, isValid bool) { + switch t := t.(type) { + case *types.Basic: + switch { + case t.Info()&types.IsBoolean != 0: + return "false", true + case t.Info()&types.IsNumeric != 0: + return "0", true + case t.Info()&types.IsString != 0: + return `""`, true + case t.Kind() == types.UnsafePointer: + fallthrough + case t.Kind() == types.UntypedNil: + return "nil", true + case t.Kind() == types.Invalid: + return "invalid", false + default: + panic(fmt.Sprintf("ZeroString for unexpected type %v", t)) + } + + case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature: + return "nil", true + + case *types.Interface: + if !t.IsMethodSet() { + return "invalid", false + } + return "nil", true + + case *types.Named: + switch under := t.Underlying().(type) { + case *types.Struct, *types.Array: + return types.TypeString(t, qual) + "{}", true + default: + return ZeroString(under, qual) + } + + case *types.Alias: + switch t.Underlying().(type) { + case *types.Struct, *types.Array: + return types.TypeString(t, qual) + "{}", true + default: + // A type parameter can have alias but alias type's underlying type + // can never be a type parameter. + // Use types.Unalias to preserve the info of type parameter instead + // of call Underlying() going right through and get the underlying + // type of the type parameter which is always an interface. + return ZeroString(types.Unalias(t), qual) + } + + case *types.Array, *types.Struct: + return types.TypeString(t, qual) + "{}", true + + case *types.TypeParam: + // Assumes func new is not shadowed. + return "*new(" + types.TypeString(t, qual) + ")", true + + case *types.Tuple: + // Tuples are not normal values. + // We are currently format as "(t[0], ..., t[n])". Could be something else. + isValid := true + components := make([]string, t.Len()) + for i := 0; i < t.Len(); i++ { + comp, ok := ZeroString(t.At(i).Type(), qual) + + components[i] = comp + isValid = isValid && ok + } + return "(" + strings.Join(components, ", ") + ")", isValid + + case *types.Union: + // Variables of these types cannot be created, so it makes + // no sense to ask for their zero value. + panic(fmt.Sprintf("invalid type for a variable: %v", t)) + + default: + panic(t) // unreachable. + } +} + +// ZeroExpr returns the ast.Expr representation of the zero value for any type t. +// The boolean result indicates whether the type is or contains an invalid type +// or a non-basic (constraint) interface type. +// +// Even for invalid input types, ZeroExpr may return a partially correct ast.Expr +// representation. The caller should use the returned isValid boolean to determine +// the validity of the expression. +// +// This function is designed for types suitable for variables and should not be +// used with Tuple or Union types.References to named types are qualified by an +// appropriate (optional) qualifier function. +// +// See [ZeroString] for a variant that returns a string. +func ZeroExpr(t types.Type, qual types.Qualifier) (_ ast.Expr, isValid bool) { + switch t := t.(type) { + case *types.Basic: + switch { + case t.Info()&types.IsBoolean != 0: + return &ast.Ident{Name: "false"}, true + case t.Info()&types.IsNumeric != 0: + return &ast.BasicLit{Kind: token.INT, Value: "0"}, true + case t.Info()&types.IsString != 0: + return &ast.BasicLit{Kind: token.STRING, Value: `""`}, true + case t.Kind() == types.UnsafePointer: + fallthrough + case t.Kind() == types.UntypedNil: + return ast.NewIdent("nil"), true + case t.Kind() == types.Invalid: + return &ast.BasicLit{Kind: token.STRING, Value: `"invalid"`}, false + default: + panic(fmt.Sprintf("ZeroExpr for unexpected type %v", t)) + } + + case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature: + return ast.NewIdent("nil"), true + + case *types.Interface: + if !t.IsMethodSet() { + return &ast.BasicLit{Kind: token.STRING, Value: `"invalid"`}, false + } + return ast.NewIdent("nil"), true + + case *types.Named: + switch under := t.Underlying().(type) { + case *types.Struct, *types.Array: + return &ast.CompositeLit{ + Type: TypeExpr(t, qual), + }, true + default: + return ZeroExpr(under, qual) + } + + case *types.Alias: + switch t.Underlying().(type) { + case *types.Struct, *types.Array: + return &ast.CompositeLit{ + Type: TypeExpr(t, qual), + }, true + default: + return ZeroExpr(types.Unalias(t), qual) + } + + case *types.Array, *types.Struct: + return &ast.CompositeLit{ + Type: TypeExpr(t, qual), + }, true + + case *types.TypeParam: + return &ast.StarExpr{ // *new(T) + X: &ast.CallExpr{ + // Assumes func new is not shadowed. + Fun: ast.NewIdent("new"), + Args: []ast.Expr{ + ast.NewIdent(t.Obj().Name()), + }, + }, + }, true + + case *types.Tuple: + // Unlike ZeroString, there is no ast.Expr can express tuple by + // "(t[0], ..., t[n])". + panic(fmt.Sprintf("invalid type for a variable: %v", t)) + + case *types.Union: + // Variables of these types cannot be created, so it makes + // no sense to ask for their zero value. + panic(fmt.Sprintf("invalid type for a variable: %v", t)) + + default: + panic(t) // unreachable. + } +} + +// TypeExpr returns syntax for the specified type. References to named types +// are qualified by an appropriate (optional) qualifier function. +// It may panic for types such as Tuple or Union. +// +// See also https://go.dev/issues/75604, which will provide a robust +// Type-to-valid-Go-syntax formatter. +func TypeExpr(t types.Type, qual types.Qualifier) ast.Expr { + switch t := t.(type) { + case *types.Basic: + switch t.Kind() { + case types.UnsafePointer: + return &ast.SelectorExpr{X: ast.NewIdent(qual(types.NewPackage("unsafe", "unsafe"))), Sel: ast.NewIdent("Pointer")} + default: + return ast.NewIdent(t.Name()) + } + + case *types.Pointer: + return &ast.UnaryExpr{ + Op: token.MUL, + X: TypeExpr(t.Elem(), qual), + } + + case *types.Array: + return &ast.ArrayType{ + Len: &ast.BasicLit{ + Kind: token.INT, + Value: fmt.Sprintf("%d", t.Len()), + }, + Elt: TypeExpr(t.Elem(), qual), + } + + case *types.Slice: + return &ast.ArrayType{ + Elt: TypeExpr(t.Elem(), qual), + } + + case *types.Map: + return &ast.MapType{ + Key: TypeExpr(t.Key(), qual), + Value: TypeExpr(t.Elem(), qual), + } + + case *types.Chan: + dir := ast.ChanDir(t.Dir()) + if t.Dir() == types.SendRecv { + dir = ast.SEND | ast.RECV + } + return &ast.ChanType{ + Dir: dir, + Value: TypeExpr(t.Elem(), qual), + } + + case *types.Signature: + var params []*ast.Field + for v := range t.Params().Variables() { + params = append(params, &ast.Field{ + Type: TypeExpr(v.Type(), qual), + Names: []*ast.Ident{ + { + Name: v.Name(), + }, + }, + }) + } + if t.Variadic() { + last := params[len(params)-1] + last.Type = &ast.Ellipsis{Elt: last.Type.(*ast.ArrayType).Elt} + } + var returns []*ast.Field + for v := range t.Results().Variables() { + returns = append(returns, &ast.Field{ + Type: TypeExpr(v.Type(), qual), + }) + } + return &ast.FuncType{ + Params: &ast.FieldList{ + List: params, + }, + Results: &ast.FieldList{ + List: returns, + }, + } + + case *types.TypeParam: + pkgName := qual(t.Obj().Pkg()) + if pkgName == "" || t.Obj().Pkg() == nil { + return ast.NewIdent(t.Obj().Name()) + } + return &ast.SelectorExpr{ + X: ast.NewIdent(pkgName), + Sel: ast.NewIdent(t.Obj().Name()), + } + + // types.TypeParam also implements interface NamedOrAlias. To differentiate, + // case TypeParam need to be present before case NamedOrAlias. + // TODO(hxjiang): remove this comment once TypeArgs() is added to interface + // NamedOrAlias. + case NamedOrAlias: + var expr ast.Expr = ast.NewIdent(t.Obj().Name()) + if pkgName := qual(t.Obj().Pkg()); pkgName != "." && pkgName != "" { + expr = &ast.SelectorExpr{ + X: ast.NewIdent(pkgName), + Sel: expr.(*ast.Ident), + } + } + + // TODO(hxjiang): call t.TypeArgs after adding method TypeArgs() to + // typesinternal.NamedOrAlias. + if hasTypeArgs, ok := t.(interface{ TypeArgs() *types.TypeList }); ok { + if typeArgs := hasTypeArgs.TypeArgs(); typeArgs != nil && typeArgs.Len() > 0 { + var indices []ast.Expr + for t0 := range typeArgs.Types() { + indices = append(indices, TypeExpr(t0, qual)) + } + expr = &ast.IndexListExpr{ + X: expr, + Indices: indices, + } + } + } + + return expr + + case *types.Struct: + return ast.NewIdent(t.String()) + + case *types.Interface: + return ast.NewIdent(t.String()) + + case *types.Union: + if t.Len() == 0 { + panic("Union type should have at least one term") + } + // Same as go/ast, the return expression will put last term in the + // Y field at topmost level of BinaryExpr. + // For union of type "float32 | float64 | int64", the structure looks + // similar to: + // { + // X: { + // X: float32, + // Op: | + // Y: float64, + // } + // Op: |, + // Y: int64, + // } + var union ast.Expr + for i := range t.Len() { + term := t.Term(i) + termExpr := TypeExpr(term.Type(), qual) + if term.Tilde() { + termExpr = &ast.UnaryExpr{ + Op: token.TILDE, + X: termExpr, + } + } + if i == 0 { + union = termExpr + } else { + union = &ast.BinaryExpr{ + X: union, + Op: token.OR, + Y: termExpr, + } + } + } + return union + + case *types.Tuple: + panic("invalid input type types.Tuple") + + default: + panic("unreachable") + } +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/versions/features.go b/vendor/honnef.co/go/tools/internal/xtools-internal/versions/features.go new file mode 100644 index 00000000..360a5b55 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/versions/features.go @@ -0,0 +1,49 @@ +// Copyright 2023 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 versions + +// This file contains predicates for working with file versions to +// decide when a tool should consider a language feature enabled. + +// named constants, to avoid misspelling +const ( + Go1_17 = "go1.17" + Go1_18 = "go1.18" + Go1_19 = "go1.19" + Go1_20 = "go1.20" + Go1_21 = "go1.21" + Go1_22 = "go1.22" + Go1_23 = "go1.23" + Go1_24 = "go1.24" + Go1_25 = "go1.25" + Go1_26 = "go1.26" + Go1_27 = "go1.27" +) + +// Future is an invalid unknown Go version sometime in the future. +// Do not use directly with Compare. +const Future = "" + +// AtLeast reports whether the file version v comes after a Go release. +// +// Use this predicate to enable a behavior once a certain Go release +// has happened (and stays enabled in the future). +func AtLeast(v, release string) bool { + if v == Future { + return true // an unknown future version is always after y. + } + return Compare(Lang(v), Lang(release)) >= 0 +} + +// Before reports whether the file version v is strictly before a Go release. +// +// Use this predicate to disable a behavior once a certain Go release +// has happened (and stays enabled in the future). +func Before(v, release string) bool { + if v == Future { + return false // an unknown future version happens after y. + } + return Compare(Lang(v), Lang(release)) < 0 +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/versions/gover.go b/vendor/honnef.co/go/tools/internal/xtools-internal/versions/gover.go new file mode 100644 index 00000000..bbabcd22 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/versions/gover.go @@ -0,0 +1,172 @@ +// Copyright 2023 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 is a fork of internal/gover for use by x/tools until +// go1.21 and earlier are no longer supported by x/tools. + +package versions + +import "strings" + +// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]] +// The numbers are the original decimal strings to avoid integer overflows +// and since there is very little actual math. (Probably overflow doesn't matter in practice, +// but at the time this code was written, there was an existing test that used +// go1.99999999999, which does not fit in an int on 32-bit platforms. +// The "big decimal" representation avoids the problem entirely.) +type gover struct { + major string // decimal + minor string // decimal or "" + patch string // decimal or "" + kind string // "", "alpha", "beta", "rc" + pre string // decimal or "" +} + +// compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as toolchain versions. +// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21". +// Malformed versions compare less than well-formed versions and equal to each other. +// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0". +func compare(x, y string) int { + vx := parse(x) + vy := parse(y) + + if c := cmpInt(vx.major, vy.major); c != 0 { + return c + } + if c := cmpInt(vx.minor, vy.minor); c != 0 { + return c + } + if c := cmpInt(vx.patch, vy.patch); c != 0 { + return c + } + if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc + return c + } + if c := cmpInt(vx.pre, vy.pre); c != 0 { + return c + } + return 0 +} + +// lang returns the Go language version. For example, lang("1.2.3") == "1.2". +func lang(x string) string { + v := parse(x) + if v.minor == "" || v.major == "1" && v.minor == "0" { + return v.major + } + return v.major + "." + v.minor +} + +// isValid reports whether the version x is valid. +func isValid(x string) bool { + return parse(x) != gover{} +} + +// parse parses the Go version string x into a version. +// It returns the zero version if x is malformed. +func parse(x string) gover { + var v gover + + // Parse major version. + var ok bool + v.major, x, ok = cutInt(x) + if !ok { + return gover{} + } + if x == "" { + // Interpret "1" as "1.0.0". + v.minor = "0" + v.patch = "0" + return v + } + + // Parse . before minor version. + if x[0] != '.' { + return gover{} + } + + // Parse minor version. + v.minor, x, ok = cutInt(x[1:]) + if !ok { + return gover{} + } + if x == "" { + // Patch missing is same as "0" for older versions. + // Starting in Go 1.21, patch missing is different from explicit .0. + if cmpInt(v.minor, "21") < 0 { + v.patch = "0" + } + return v + } + + // Parse patch if present. + if x[0] == '.' { + v.patch, x, ok = cutInt(x[1:]) + if !ok || x != "" { + // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != ""). + // Allowing them would be a bit confusing because we already have: + // 1.21 < 1.21rc1 + // But a prerelease of a patch would have the opposite effect: + // 1.21.3rc1 < 1.21.3 + // We've never needed them before, so let's not start now. + return gover{} + } + return v + } + + // Parse prerelease. + i := 0 + for i < len(x) && (x[i] < '0' || '9' < x[i]) { + if x[i] < 'a' || 'z' < x[i] { + return gover{} + } + i++ + } + if i == 0 { + return gover{} + } + v.kind, x = x[:i], x[i:] + if x == "" { + return v + } + v.pre, x, ok = cutInt(x) + if !ok || x != "" { + return gover{} + } + + return v +} + +// cutInt scans the leading decimal number at the start of x to an integer +// and returns that value and the rest of the string. +func cutInt(x string) (n, rest string, ok bool) { + i := 0 + for i < len(x) && '0' <= x[i] && x[i] <= '9' { + i++ + } + if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero + return "", "", false + } + return x[:i], x[i:], true +} + +// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers. +// (Copied from golang.org/x/mod/semver's compareInt.) +func cmpInt(x, y string) int { + if x == y { + return 0 + } + if len(x) < len(y) { + return -1 + } + if len(x) > len(y) { + return +1 + } + if x < y { + return -1 + } else { + return +1 + } +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/versions/types.go b/vendor/honnef.co/go/tools/internal/xtools-internal/versions/types.go new file mode 100644 index 00000000..0fc10ce4 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/versions/types.go @@ -0,0 +1,33 @@ +// Copyright 2023 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 versions + +import ( + "go/ast" + "go/types" +) + +// FileVersion returns a file's Go version. +// The reported version is an unknown Future version if a +// version cannot be determined. +func FileVersion(info *types.Info, file *ast.File) string { + // In tools built with Go >= 1.22, the Go version of a file + // follow a cascades of sources: + // 1) types.Info.FileVersion, which follows the cascade: + // 1.a) file version (ast.File.GoVersion), + // 1.b) the package version (types.Config.GoVersion), or + // 2) is some unknown Future version. + // + // File versions require a valid package version to be provided to types + // in Config.GoVersion. Config.GoVersion is either from the package's module + // or the toolchain (go run). This value should be provided by go/packages + // or unitchecker.Config.GoVersion. + if v := info.FileVersions[file]; IsValid(v) { + return v + } + // Note: we could instead return runtime.Version() [if valid]. + // This would act as a max version on what a tool can support. + return Future +} diff --git a/vendor/honnef.co/go/tools/internal/xtools-internal/versions/versions.go b/vendor/honnef.co/go/tools/internal/xtools-internal/versions/versions.go new file mode 100644 index 00000000..8d1f7453 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/xtools-internal/versions/versions.go @@ -0,0 +1,57 @@ +// Copyright 2023 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 versions + +import ( + "strings" +) + +// Note: If we use build tags to use go/versions when go >=1.22, +// we run into go.dev/issue/53737. Under some operations users would see an +// import of "go/versions" even if they would not compile the file. +// For example, during `go get -u ./...` (go.dev/issue/64490) we do not try to include +// For this reason, this library just a clone of go/versions for the moment. + +// Lang returns the Go language version for version x. +// If x is not a valid version, Lang returns the empty string. +// For example: +// +// Lang("go1.21rc2") = "go1.21" +// Lang("go1.21.2") = "go1.21" +// Lang("go1.21") = "go1.21" +// Lang("go1") = "go1" +// Lang("bad") = "" +// Lang("1.21") = "" +func Lang(x string) string { + v := lang(stripGo(x)) + if v == "" { + return "" + } + return x[:2+len(v)] // "go"+v without allocation +} + +// Compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as Go versions. +// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21". +// Invalid versions, including the empty string, compare less than +// valid versions and equal to each other. +// The language version "go1.21" compares less than the +// release candidate and eventual releases "go1.21rc1" and "go1.21.0". +// Custom toolchain suffixes are ignored during comparison: +// "go1.21.0" and "go1.21.0-bigcorp" are equal. +func Compare(x, y string) int { return compare(stripGo(x), stripGo(y)) } + +// IsValid reports whether the version x is valid. +func IsValid(x string) bool { return isValid(stripGo(x)) } + +// stripGo converts from a "go1.21" version to a "1.21" version. +// If v does not start with "go", stripGo returns the empty string (a known invalid version). +func stripGo(v string) string { + v, _, _ = strings.Cut(v, "-") // strip -bigcorp suffix. + if len(v) < 2 || v[:2] != "go" { + return "" + } + return v[2:] +} diff --git a/vendor/honnef.co/go/tools/knowledge/arg.go b/vendor/honnef.co/go/tools/knowledge/arg.go index 4fab2eba..c477ec4f 100644 --- a/vendor/honnef.co/go/tools/knowledge/arg.go +++ b/vendor/honnef.co/go/tools/knowledge/arg.go @@ -28,6 +28,7 @@ var Args = map[string]int{ "fmt.Sprintf.a[0]": 1, "fmt.Sprintf.format": 0, "json.Marshal.v": 0, + "json.MarshalIndent.v": 0, "json.Unmarshal.v": 1, "len.v": 0, "make.size[0]": 1, @@ -60,6 +61,7 @@ var Args = map[string]int{ "time.Parse.layout": 0, "time.Sleep.d": 0, "xml.Marshal.v": 0, + "xml.MarshalIndent.v": 0, "xml.Unmarshal.v": 1, } diff --git a/vendor/honnef.co/go/tools/knowledge/deprecated.go b/vendor/honnef.co/go/tools/knowledge/deprecated.go index 89ff8d9e..256f3756 100644 --- a/vendor/honnef.co/go/tools/knowledge/deprecated.go +++ b/vendor/honnef.co/go/tools/knowledge/deprecated.go @@ -246,6 +246,11 @@ var StdlibDeprecations = map[string]Deprecation{ "crypto/rsa.EncryptPKCS1v15": {"go1.26", DeprecatedNeverUse}, "(net/http/httputil.ReverseProxy).Director": {"go1.26", "go1.20"}, + + // Note that the available alternative, testing/cryptotest.SetGlobalRandom + // is only an alternative for testing, not all possible uses of the + // deprecated field. + "(crypto/tls.Config).Rand": {"go1.27", "go1.26"}, } // Last imported from GOROOT/api/go1.26.txt at d3ddc4854429185e6e06ca1f7628bb790404abb5. diff --git a/vendor/honnef.co/go/tools/quickfix/qf1002/qf1002.go b/vendor/honnef.co/go/tools/quickfix/qf1002/qf1002.go index 84b84723..dd522167 100644 --- a/vendor/honnef.co/go/tools/quickfix/qf1002/qf1002.go +++ b/vendor/honnef.co/go/tools/quickfix/qf1002/qf1002.go @@ -121,7 +121,7 @@ func run(pass *analysis.Pass) (any, error) { } func findSwitchPairs(pass *analysis.Pass, expr ast.Expr, pairs *[]*ast.BinaryExpr) bool { - binexpr, ok := astutil.Unparen(expr).(*ast.BinaryExpr) + binexpr, ok := ast.Unparen(expr).(*ast.BinaryExpr) if !ok { return false } diff --git a/vendor/honnef.co/go/tools/quickfix/qf1003/qf1003.go b/vendor/honnef.co/go/tools/quickfix/qf1003/qf1003.go index f8be70d4..7958795a 100644 --- a/vendor/honnef.co/go/tools/quickfix/qf1003/qf1003.go +++ b/vendor/honnef.co/go/tools/quickfix/qf1003/qf1003.go @@ -180,7 +180,7 @@ nodeLoop: } func findSwitchPairs(pass *analysis.Pass, expr ast.Expr, pairs *[]*ast.BinaryExpr) bool { - binexpr, ok := astutil.Unparen(expr).(*ast.BinaryExpr) + binexpr, ok := ast.Unparen(expr).(*ast.BinaryExpr) if !ok { return false } diff --git a/vendor/honnef.co/go/tools/quickfix/qf1004/qf1004.go b/vendor/honnef.co/go/tools/quickfix/qf1004/qf1004.go index e18c4d53..bde8c671 100644 --- a/vendor/honnef.co/go/tools/quickfix/qf1004/qf1004.go +++ b/vendor/honnef.co/go/tools/quickfix/qf1004/qf1004.go @@ -8,8 +8,8 @@ import ( "honnef.co/go/tools/analysis/edit" "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" - typeindexanalyzer "honnef.co/go/tools/internal/analysisinternal/typeindex" - "honnef.co/go/tools/internal/typesinternal/typeindex" + typeindexanalyzer "honnef.co/go/tools/internal/xtools-internal/analysis/typeindex" + "honnef.co/go/tools/internal/xtools-internal/typesinternal/typeindex" "golang.org/x/tools/go/analysis" ) diff --git a/vendor/honnef.co/go/tools/simple/s1005/s1005.go b/vendor/honnef.co/go/tools/simple/s1005/s1005.go index 466f38a4..bd3dcbdd 100644 --- a/vendor/honnef.co/go/tools/simple/s1005/s1005.go +++ b/vendor/honnef.co/go/tools/simple/s1005/s1005.go @@ -27,11 +27,9 @@ var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Text: `In many cases, assigning to the blank identifier is unnecessary.`, Before: ` for _ = range s {} -x, _ = someMap[key] _ = <-ch`, After: ` for range s{} -x = someMap[key] <-ch`, Since: "2017.1", MergeIf: lint.MergeIfAny, @@ -45,9 +43,7 @@ var ( (AssignStmt [_ (Ident "_")] _ - (Or - (IndexExpr _ _) - (UnaryExpr "<-" _))) `) + (UnaryExpr "<-" _)) `) checkUnnecessaryBlankQ2 = pattern.MustParse(` (AssignStmt (Ident "_") _ recv@(UnaryExpr "<-" _))`) @@ -55,6 +51,10 @@ var ( func run(pass *analysis.Pass) (any, error) { fn1 := func(node ast.Node) { + // We don't check for 'x, _ = m[k]', which might be used to indicate + // that one knows that there might be no entry and that one doesn't + // care. + if _, ok := code.Match(pass, checkUnnecessaryBlankQ1, node); ok { r := *node.(*ast.AssignStmt) r.Lhs = r.Lhs[0:1] diff --git a/vendor/honnef.co/go/tools/staticcheck/analysis.go b/vendor/honnef.co/go/tools/staticcheck/analysis.go index fe8642ab..81ab81c9 100644 --- a/vendor/honnef.co/go/tools/staticcheck/analysis.go +++ b/vendor/honnef.co/go/tools/staticcheck/analysis.go @@ -82,7 +82,6 @@ import ( "honnef.co/go/tools/staticcheck/sa5008" "honnef.co/go/tools/staticcheck/sa5009" "honnef.co/go/tools/staticcheck/sa5010" - "honnef.co/go/tools/staticcheck/sa5011" "honnef.co/go/tools/staticcheck/sa5012" "honnef.co/go/tools/staticcheck/sa6000" "honnef.co/go/tools/staticcheck/sa6001" @@ -99,6 +98,7 @@ import ( "honnef.co/go/tools/staticcheck/sa9007" "honnef.co/go/tools/staticcheck/sa9008" "honnef.co/go/tools/staticcheck/sa9009" + "honnef.co/go/tools/staticcheck/sa9010" ) var Analyzers = []*lint.Analyzer{ @@ -180,7 +180,6 @@ var Analyzers = []*lint.Analyzer{ sa5008.SCAnalyzer, sa5009.SCAnalyzer, sa5010.SCAnalyzer, - sa5011.SCAnalyzer, sa5012.SCAnalyzer, sa6000.SCAnalyzer, sa6001.SCAnalyzer, @@ -197,4 +196,5 @@ var Analyzers = []*lint.Analyzer{ sa9007.SCAnalyzer, sa9008.SCAnalyzer, sa9009.SCAnalyzer, + sa9010.SCAnalyzer, } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa1002/sa1002.go b/vendor/honnef.co/go/tools/staticcheck/sa1002/sa1002.go index ddc20767..4f7159af 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa1002/sa1002.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa1002/sa1002.go @@ -20,7 +20,10 @@ var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Run: callcheck.Analyzer(rules), }, Doc: &lint.RawDocumentation{ - Title: `Invalid format in \'time.Parse\'`, + Title: `Invalid format in \'time.Parse\'`, + Text: `\'time.Parse\' requires a layout string that uses Go's reference time: +\"Mon Jan 2 15:04:05 MST 2006\". The layout must represent this date and time +exactly. See https://pkg.go.dev/time#pkg-constants for layout examples.`, Since: "2017.1", Severity: lint.SeverityError, MergeIf: lint.MergeIfAny, diff --git a/vendor/honnef.co/go/tools/staticcheck/sa1012/sa1012.go b/vendor/honnef.co/go/tools/staticcheck/sa1012/sa1012.go index 16bd0798..b04fd8c8 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa1012/sa1012.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa1012/sa1012.go @@ -21,7 +21,10 @@ var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Requires: code.RequiredAnalyzers, }, Doc: &lint.RawDocumentation{ - Title: `A nil \'context.Context\' is being passed to a function, consider using \'context.TODO\' instead`, + Title: `A nil \'context.Context\' is being passed to a function, consider using \'context.TODO\' instead`, + Text: `The context package prohibits the use of a \'nil\' context. +If no parent context is available, a new context should be used, +e.g. \'context.TODO\' or \'context.Background\'.`, Since: "2017.1", Severity: lint.SeverityWarning, MergeIf: lint.MergeIfAny, diff --git a/vendor/honnef.co/go/tools/staticcheck/sa1014/sa1014.go b/vendor/honnef.co/go/tools/staticcheck/sa1014/sa1014.go index 90d676af..528aa339 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa1014/sa1014.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa1014/sa1014.go @@ -18,7 +18,11 @@ var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Run: callcheck.Analyzer(checkUnmarshalPointerRules), }, Doc: &lint.RawDocumentation{ - Title: `Non-pointer value passed to \'Unmarshal\' or \'Decode\'`, + Title: `Non-pointer value passed to \'Unmarshal\' or \'Decode\'`, + Text: `Functions such as \'encoding/json.Unmarshal\' and +\'(*encoding/json.Decoder).Decode\' require a pointer to the value that should +be populated. Passing a non-pointer value results in the function returning an +error at runtime, as it cannot modify the target value.`, Since: "2017.1", Severity: lint.SeverityError, MergeIf: lint.MergeIfAny, diff --git a/vendor/honnef.co/go/tools/staticcheck/sa1019/sa1019.go b/vendor/honnef.co/go/tools/staticcheck/sa1019/sa1019.go index 97040e8f..0fb9b307 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa1019/sa1019.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa1019/sa1019.go @@ -99,50 +99,33 @@ func run(pass *analysis.Pass) (any, error) { case knowledge.DeprecatedNeverUse: report.Report(pass, node, fmt.Sprintf("%s has been deprecated since %s because it shouldn't be used: %s", - report.Render(pass, node), formatGoVersion(std.DeprecatedSince), depr.Msg)) + deprecatedObjName, formatGoVersion(std.DeprecatedSince), depr.Msg)) case std.DeprecatedSince, knowledge.DeprecatedUseNoLonger: report.Report(pass, node, fmt.Sprintf("%s has been deprecated since %s: %s", - report.Render(pass, node), formatGoVersion(std.DeprecatedSince), depr.Msg)) + deprecatedObjName, formatGoVersion(std.DeprecatedSince), depr.Msg)) default: report.Report(pass, node, fmt.Sprintf("%s has been deprecated since %s and an alternative has been available since %s: %s", - report.Render(pass, node), formatGoVersion(std.DeprecatedSince), formatGoVersion(std.AlternativeAvailableSince), depr.Msg)) + deprecatedObjName, formatGoVersion(std.DeprecatedSince), formatGoVersion(std.AlternativeAvailableSince), depr.Msg)) } } else { - report.Report(pass, node, fmt.Sprintf("%s is deprecated: %s", report.Render(pass, node), depr.Msg)) + report.Report(pass, node, fmt.Sprintf("%s is deprecated: %s", deprecatedObjName, depr.Msg)) } } var tfn types.Object stack := 0 - fn := func(node ast.Node, push bool) bool { - if !push { - stack-- - return false - } - stack++ - if stack == 1 { - tfn = nil - } - if fn, ok := node.(*ast.FuncDecl); ok { - tfn = pass.TypesInfo.ObjectOf(fn.Name) - } - - // FIXME(dh): this misses dot-imported objects - sel, ok := node.(*ast.SelectorExpr) - if !ok { - return true - } + checkIdentObj := func(sel *ast.SelectorExpr) bool { obj := pass.TypesInfo.ObjectOf(sel.Sel) + if obj_, ok := obj.(*types.Func); ok { obj = obj_.Origin() } if obj.Pkg() == nil { return true } - if obj.Pkg() == pass.Pkg { // A package is allowed to use its own deprecated objects return true @@ -165,8 +148,59 @@ func run(pass *analysis.Pass) (any, error) { return true } + node := ast.Node(sel) + if pass.TypesInfo.Types[sel.X].IsType() { + node = sel.Sel + } if depr, ok := deprs.Objects[obj]; ok { - handleDeprecation(depr, sel, code.SelectorName(pass, sel), obj.Pkg().Path(), tfn) + handleDeprecation(depr, node, code.SelectorName(pass, sel), obj.Pkg().Path(), tfn) + } + return true + } + + fn := func(node ast.Node, push bool) bool { + if !push { + stack-- + return false + } + stack++ + if stack == 1 { + tfn = nil + } + if fn, ok := node.(*ast.FuncDecl); ok { + tfn = pass.TypesInfo.ObjectOf(fn.Name) + } + + switch v := node.(type) { + // FIXME(dh): this misses dot-imported objects + case *ast.SelectorExpr: + return checkIdentObj(v) + + case *ast.CompositeLit: + litType := pass.TypesInfo.Types[v.Type] + if !litType.IsType() { + // This is probably unreachable. + return true + } + if _, ok := litType.Type.Underlying().(*types.Struct); !ok { + // We don't want to look at expressions in map initializers, for + // example. + return true + } + for _, elt := range v.Elts { + kv, ok := elt.(*ast.KeyValueExpr) + if !ok { + return true + } + key, ok := kv.Key.(*ast.Ident) + if !ok { + // This is probably unreachable, since we're looking at keys + // in a struct initializer. + return true + } + sel := &ast.SelectorExpr{X: v.Type, Sel: key} + checkIdentObj(sel) + } } return true } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa1020/sa1020.go b/vendor/honnef.co/go/tools/staticcheck/sa1020/sa1020.go index aa1aeae6..9d6256a2 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa1020/sa1020.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa1020/sa1020.go @@ -20,7 +20,12 @@ var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Run: callcheck.Analyzer(checkListenAddressRules), }, Doc: &lint.RawDocumentation{ - Title: `Using an invalid host:port pair with a \'net.Listen\'-related function`, + Title: `Using an invalid host:port pair with a \'net.Listen\'-related function`, + Text: `Functions such as \'net.Listen\', \'net.ListenTCP\', and similar, +expect a valid network address in the form of host:port. The host, the port, +or both, can be omitted, e.g. \'localhost:8080\', \':8080\' or \':\' are valid +host:port pairs. +See https://pkg.go.dev/net#Listen for the full documentation.`, Since: "2017.1", Severity: lint.SeverityError, MergeIf: lint.MergeIfAny, diff --git a/vendor/honnef.co/go/tools/staticcheck/sa1025/sa1025.go b/vendor/honnef.co/go/tools/staticcheck/sa1025/sa1025.go index 9b7b6afc..2ac361ae 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa1025/sa1025.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa1025/sa1025.go @@ -43,7 +43,7 @@ func run(pass *analysis.Pass) (any, error) { if refs == nil { continue } - for _, ref := range irutil.FilterDebug(*refs) { + for _, ref := range *refs { ifstmt, ok := ref.(*ir.If) if !ok { continue diff --git a/vendor/honnef.co/go/tools/staticcheck/sa1026/sa1026.go b/vendor/honnef.co/go/tools/staticcheck/sa1026/sa1026.go index 0c685c3a..0aa99fc6 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa1026/sa1026.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa1026/sa1026.go @@ -31,7 +31,9 @@ var Analyzer = SCAnalyzer.Analyzer var rules = map[string]callcheck.Check{ "encoding/json.Marshal": checkJSON, + "encoding/json.MarshalIndent": checkJSON, "encoding/xml.Marshal": checkXML, + "encoding/xml.MarshalIndent": checkXML, "(*encoding/json.Encoder).Encode": checkJSON, "(*encoding/xml.Encoder).Encode": checkXML, } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa1030/sa1030.go b/vendor/honnef.co/go/tools/staticcheck/sa1030/sa1030.go index 1a274ce1..05069387 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa1030/sa1030.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa1030/sa1030.go @@ -128,7 +128,7 @@ func validateFloatFormat(arg *callcheck.Argument) { switch val { case 'b', 'e', 'E', 'f', 'g', 'G', 'x', 'X': default: - arg.Invalid(fmt.Sprintf("'fmt' argument is invalid: unknown format %q", val)) + arg.Invalid(fmt.Sprintf("'fmt' argument is invalid: unknown format %q", rune(val))) } } } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa2000/sa2000.go b/vendor/honnef.co/go/tools/staticcheck/sa2000/sa2000.go index 00874292..721f269d 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa2000/sa2000.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa2000/sa2000.go @@ -19,7 +19,10 @@ var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Requires: code.RequiredAnalyzers, }, Doc: &lint.RawDocumentation{ - Title: `\'sync.WaitGroup.Add\' called inside the goroutine, leading to a race condition`, + Title: `\'(*sync.WaitGroup).Add\' called inside the goroutine, leading to a race condition`, + Text: `\'(*sync.WaitGroup).Add\' must be called before starting the goroutine +it is meant to wait for. Calling \'Add\' inside the goroutine creates a race +condition between the call to \'Add\' and the call to \'Wait\'.`, Since: "2017.1", Severity: lint.SeverityWarning, MergeIf: lint.MergeIfAny, diff --git a/vendor/honnef.co/go/tools/staticcheck/sa2001/sa2001.go b/vendor/honnef.co/go/tools/staticcheck/sa2001/sa2001.go index e248250f..5e3f83e1 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa2001/sa2001.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa2001/sa2001.go @@ -7,7 +7,6 @@ import ( "honnef.co/go/tools/analysis/code" "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" - "honnef.co/go/tools/go/ast/astutil" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" @@ -67,7 +66,7 @@ func run(pass *analysis.Pass) (any, error) { if !ok { return nil, "", false } - call, ok := astutil.Unparen(expr.X).(*ast.CallExpr) + call, ok := ast.Unparen(expr.X).(*ast.CallExpr) if !ok { return nil, "", false } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa2003/sa2003.go b/vendor/honnef.co/go/tools/staticcheck/sa2003/sa2003.go index a7163493..c6fb7f0f 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa2003/sa2003.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa2003/sa2003.go @@ -20,7 +20,19 @@ var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Requires: []*analysis.Analyzer{buildir.Analyzer}, }, Doc: &lint.RawDocumentation{ - Title: `Deferred \'Lock\' right after locking, likely meant to defer \'Unlock\' instead`, + Title: `Deferred \'Lock\' right after locking, likely meant to defer \'Unlock\' instead`, + Text: `Deferring a call to \'Lock\' immediately after locking is almost always +a typo. For example: + + mu.Lock() + defer mu.Lock() + +While this does not strictly guarantee a deadlock depending on how the +surrounding code is structured, it is highly likely to be a mistake. +The intended code was likely this: + + mu.Lock() + defer mu.Unlock()`, Since: "2017.1", Severity: lint.SeverityWarning, MergeIf: lint.MergeIfAny, @@ -32,7 +44,7 @@ var Analyzer = SCAnalyzer.Analyzer func run(pass *analysis.Pass) (any, error) { for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { for _, block := range fn.Blocks { - instrs := irutil.FilterDebug(block.Instrs) + instrs := block.Instrs if len(instrs) < 2 { continue } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa4003/sa4003.go b/vendor/honnef.co/go/tools/staticcheck/sa4003/sa4003.go index a399c338..eb36c418 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa4003/sa4003.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa4003/sa4003.go @@ -49,12 +49,10 @@ func run(pass *analysis.Pass) (any, error) { fn := func(node ast.Node) { expr := node.(*ast.BinaryExpr) tx := pass.TypesInfo.TypeOf(expr.X) - basic, ok := tx.Underlying().(*types.Basic) - if !ok { - return - } + tset := typeutil.NewTypeSet(tx) - // We only check for the math constants and integer literals, not for all constant expressions. This is to avoid + // We only check for the math constants and integer literals, not for + // all constant expressions. This is to avoid // false positives when constant values differ under different build tags. var ( maxMathConst string @@ -63,6 +61,54 @@ func run(pass *analysis.Pass) (any, error) { minLiteral constant.Value ) + allUnsigned := tset.All(func(t *types.Term) bool { + if basic, ok := t.Type().Underlying().(*types.Basic); ok { + return basic.Info()&types.IsUnsigned != 0 + } + return false + }) + + if allUnsigned { + isZeroLiteral := func(expr ast.Expr) bool { + return code.IsIntegerLiteral(pass, expr, constant.MakeInt64(0)) + } + if (expr.Op == token.LSS && isZeroLiteral(expr.Y)) || + (expr.Op == token.GTR && isZeroLiteral(expr.X)) { + report.Report( + pass, + expr, + fmt.Sprintf("no value of type %s is less than 0", tx), + report.FilterGenerated(), + ) + } + if expr.Op == token.GEQ && isZeroLiteral(expr.Y) || + expr.Op == token.LEQ && isZeroLiteral(expr.X) { + report.Report( + pass, + expr, + fmt.Sprintf("every value of type %s is >= 0", tx), + report.FilterGenerated(), + ) + } + } + + core := tset.CoreType() + if core == nil { + // All remaining checks are only relevant when the type set + // contains a single underlying type. + // + // If we had a 'var x uint8 | uint16', + // then the type checker wouldn't permit a check such as + // 'if x <= math.MaxUint16', because the constant cannot be converted to all + // types in the type set. + return + } + + basic, ok := core.(*types.Basic) + if !ok { + return + } + switch basic.Kind() { case types.Uint8: maxMathConst = "math.MaxUint8" @@ -81,7 +127,8 @@ func run(pass *analysis.Pass) (any, error) { minLiteral = constant.MakeUint64(0) maxLiteral = constant.MakeUint64(math.MaxUint64) case types.Uint: - // TODO(dh): we could chose 32 bit vs 64 bit depending on the file's build tags + // TODO(dh): we could chose 32 bit vs 64 bit depending on the + // file's build tags maxMathConst = "math.MaxUint64" minLiteral = constant.MakeUint64(0) maxLiteral = constant.MakeUint64(math.MaxUint64) @@ -107,7 +154,8 @@ func run(pass *analysis.Pass) (any, error) { minLiteral = constant.MakeInt64(math.MinInt64) maxLiteral = constant.MakeInt64(math.MaxInt64) case types.Int: - // TODO(dh): we could chose 32 bit vs 64 bit depending on the file's build tags + // TODO(dh): we could chose 32 bit vs 64 bit depending on the + // file's build tags minMathConst = "math.MinInt64" maxMathConst = "math.MaxInt64" minLiteral = constant.MakeInt64(math.MinInt64) @@ -120,37 +168,53 @@ func run(pass *analysis.Pass) (any, error) { } return code.IsIntegerLiteral(pass, expr, c) } - isZeroLiteral := func(expr ast.Expr) bool { - return code.IsIntegerLiteral(pass, expr, constant.MakeInt64(0)) - } - if (expr.Op == token.GTR || expr.Op == token.GEQ) && (isobj(expr.Y, maxMathConst) || isLiteral(expr.Y, maxLiteral)) || - (expr.Op == token.LSS || expr.Op == token.LEQ) && (isobj(expr.X, maxMathConst) || isLiteral(expr.X, maxLiteral)) { - report.Report(pass, expr, fmt.Sprintf("no value of type %s is greater than %s", basic, maxMathConst), report.FilterGenerated()) + x, y, op := expr.X, expr.Y, expr.Op + switch op { + case token.GEQ, token.GTR: + case token.LEQ: + x, y = y, x + op = token.GEQ + case token.LSS: + x, y = y, x + op = token.GTR + default: + return } - if expr.Op == token.LEQ && (isobj(expr.Y, maxMathConst) || isLiteral(expr.Y, maxLiteral)) || - expr.Op == token.GEQ && (isobj(expr.X, maxMathConst) || isLiteral(expr.X, maxLiteral)) { - report.Report(pass, expr, fmt.Sprintf("every value of type %s is <= %s", basic, maxMathConst), report.FilterGenerated()) + if isobj(y, maxMathConst) || isLiteral(y, maxLiteral) { + report.Report( + pass, + expr, + fmt.Sprintf("no value of type %s is greater than %s", tx, maxMathConst), + report.FilterGenerated(), + ) + } + if op == token.GEQ && (isobj(x, maxMathConst) || isLiteral(x, maxLiteral)) { + report.Report( + pass, + expr, + fmt.Sprintf("every value of type %s is <= %s", tx, maxMathConst), + report.FilterGenerated(), + ) } - if (basic.Info() & types.IsUnsigned) != 0 { - if (expr.Op == token.LSS && isZeroLiteral(expr.Y)) || - (expr.Op == token.GTR && isZeroLiteral(expr.X)) { - report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than 0", basic), report.FilterGenerated()) - } - if expr.Op == token.GEQ && isZeroLiteral(expr.Y) || - expr.Op == token.LEQ && isZeroLiteral(expr.X) { - report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= 0", basic), report.FilterGenerated()) - } - } else { - if (expr.Op == token.LSS || expr.Op == token.LEQ) && (isobj(expr.Y, minMathConst) || isLiteral(expr.Y, minLiteral)) || - (expr.Op == token.GTR || expr.Op == token.GEQ) && (isobj(expr.X, minMathConst) || isLiteral(expr.X, minLiteral)) { - report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than %s", basic, minMathConst), report.FilterGenerated()) + if !allUnsigned { + if isobj(x, minMathConst) || isLiteral(x, minLiteral) { + report.Report( + pass, + expr, + fmt.Sprintf("no value of type %s is less than %s", tx, minMathConst), + report.FilterGenerated(), + ) } - if expr.Op == token.GEQ && (isobj(expr.Y, minMathConst) || isLiteral(expr.Y, minLiteral)) || - expr.Op == token.LEQ && (isobj(expr.X, minMathConst) || isLiteral(expr.X, minLiteral)) { - report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= %s", basic, minMathConst), report.FilterGenerated()) + if op == token.GEQ && (isobj(y, minMathConst) || isLiteral(y, minLiteral)) { + report.Report( + pass, + expr, + fmt.Sprintf("every value of type %s is >= %s", tx, minMathConst), + report.FilterGenerated(), + ) } } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa4005/sa4005.go b/vendor/honnef.co/go/tools/staticcheck/sa4005/sa4005.go index f900ed21..c46a038e 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa4005/sa4005.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa4005/sa4005.go @@ -50,7 +50,7 @@ fnLoop: } recv := fn.Params[0] - refs := irutil.FilterDebug(*recv.Referrers()) + refs := *recv.Referrers() if len(refs) != 1 { continue } @@ -74,8 +74,6 @@ fnLoop: writes[ref.Field] = append(writes[ref.Field], refref) case *ir.Load: reads[ref.Field] = append(reads[ref.Field], refref) - case *ir.DebugRef: - continue default: // this should be safe… if the field address // escapes, then alloc.Heap will be true. @@ -98,8 +96,6 @@ fnLoop: for i := 0; i < recv.Type().Underlying().(*types.Struct).NumFields(); i++ { reads[i] = append(reads[i], ref) } - case *ir.DebugRef: - continue default: continue fnLoop } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa4006/sa4006.go b/vendor/honnef.co/go/tools/staticcheck/sa4006/sa4006.go index dac27b75..04865002 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa4006/sa4006.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa4006/sa4006.go @@ -3,6 +3,7 @@ package sa4006 import ( "fmt" "go/ast" + "go/token" "honnef.co/go/tools/analysis/code" "honnef.co/go/tools/analysis/facts/generated" @@ -74,15 +75,6 @@ func run(pass *analysis.Pass) (any, error) { } for _, ref := range *refs { switch ref := ref.(type) { - case *ir.DebugRef: - case *ir.Sigma: - if seen == nil { - seen = map[ir.Value]struct{}{} - } - seen[v] = struct{}{} - if hasUse(ref, seen) { - return true - } case *ir.Phi: if seen == nil { seen = map[ir.Value]struct{}{} @@ -99,6 +91,22 @@ func run(pass *analysis.Pass) (any, error) { } ast.Inspect(node, func(node ast.Node) bool { + inc, ok := node.(*ast.IncDecStmt) + if ok { + val, _ := fn.ValueForExpr(inc.X) + if val == nil { + return true + } + if _, ok := val.(*ir.Const); ok { + // a zero-valued constant, for example in 'foo := []string(nil)' + return true + } + if !hasUse(val, nil) { + report.Report(pass, inc, fmt.Sprintf("this value of %s is never used", inc.X)) + } + return true + } + assign, ok := node.(*ast.AssignStmt) if !ok { return true @@ -137,7 +145,12 @@ func run(pass *analysis.Pass) (any, error) { } val, _ := fn.ValueForExpr(rhs) if val == nil { - continue + if assign.Tok != token.ASSIGN { // +=, *=, etc. + val, _ = fn.ValueForExpr(lhs) + } + if val == nil { + continue + } } if _, ok := val.(*ir.Const); ok { diff --git a/vendor/honnef.co/go/tools/staticcheck/sa4008/sa4008.go b/vendor/honnef.co/go/tools/staticcheck/sa4008/sa4008.go index 41c56e35..f6c89eee 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa4008/sa4008.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa4008/sa4008.go @@ -82,23 +82,9 @@ func run(pass *analysis.Pass) (any, error) { if v == nil || isAddr { return true } - switch v := v.(type) { + switch v.(type) { case *ir.Phi: - ops := v.Operands(nil) - if len(ops) != 2 { - return true - } - _, ok := (*ops[0]).(*ir.Const) - if !ok { - return true - } - sigma, ok := (*ops[1]).(*ir.Sigma) - if !ok { - return true - } - if sigma.X != v { - return true - } + return true case *ir.Load: return true } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa4009/sa4009.go b/vendor/honnef.co/go/tools/staticcheck/sa4009/sa4009.go index 8ae8dd27..7c94d5e3 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa4009/sa4009.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa4009/sa4009.go @@ -7,7 +7,6 @@ import ( "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" "honnef.co/go/tools/go/ir" - "honnef.co/go/tools/go/ir/irutil" "honnef.co/go/tools/internal/passes/buildir" "golang.org/x/tools/go/analysis" @@ -65,7 +64,7 @@ func run(pass *analysis.Pass) (any, error) { if refs == nil { continue } - if len(irutil.FilterDebug(*refs)) != 0 { + if len(*refs) != 0 { continue } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa4010/sa4010.go b/vendor/honnef.co/go/tools/staticcheck/sa4010/sa4010.go index 2e11e6f2..f445aa7c 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa4010/sa4010.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa4010/sa4010.go @@ -16,7 +16,10 @@ var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Requires: []*analysis.Analyzer{buildir.Analyzer}, }, Doc: &lint.RawDocumentation{ - Title: `The result of \'append\' will never be observed anywhere`, + Title: `The result of \'append\' will never be observed anywhere`, + Text: `Calls to \'append\' produce a new slice value. When the result of +\'append\' is assigned to a variable that is never subsequently read, the +append operation may have an unintended effect.`, Since: "2017.1", Severity: lint.SeverityWarning, MergeIf: lint.MergeIfAll, @@ -54,7 +57,6 @@ func run(pass *analysis.Pass) (any, error) { // This graph must only consist of the following instructions: // // - phi - // - sigma // - slice // - const nil // - MakeSlice @@ -62,8 +64,7 @@ func run(pass *analysis.Pass) (any, error) { // - calls to append // // If this step succeeds, we look at all referrers of the values found in the first step, recursively. - // These referrers must either be in the set of values found in the first step, - // be DebugRefs, + // These referrers must either be in the set of values found in the first step // or fulfill the same type requirements as step 1, with the exception of appends, which are forbidden. // // If both steps succeed then we know that the backing array hasn't been aliased in an observable manner. @@ -90,8 +91,6 @@ func run(pass *analysis.Pass) (any, error) { } } return true - case *ir.Sigma: - return validateArgument(v.X, seen) case *ir.Slice: return validateArgument(v.X, seen) case *ir.Const: @@ -112,28 +111,27 @@ func run(pass *analysis.Pass) (any, error) { var validateReferrers func(v ir.Value, seen map[ir.Instruction]struct{}) bool validateReferrers = func(v ir.Value, seen map[ir.Instruction]struct{}) bool { - for _, ref := range *v.Referrers() { - if _, ok := seen[ref]; ok { - continue - } - - seen[ref] = struct{}{} - switch ref.(type) { - case *ir.Phi: - case *ir.Sigma: - case *ir.Slice: - case *ir.Const: - case *ir.MakeSlice: - case *ir.Alloc: - case *ir.DebugRef: - default: - return false - } + if refs := v.Referrers(); refs != nil { + for _, ref := range *refs { + if _, ok := seen[ref]; ok { + continue + } - if ref, ok := ref.(ir.Value); ok { - if !validateReferrers(ref, seen) { + seen[ref] = struct{}{} + switch ref.(type) { + case *ir.Phi: + case *ir.Slice: + case *ir.MakeSlice: + case *ir.Alloc: + default: return false } + + if ref, ok := ref.(ir.Value); ok { + if !validateReferrers(ref, seen) { + return false + } + } } } return true @@ -157,14 +155,9 @@ func run(pass *analysis.Pass) (any, error) { continue } visited[ref] = true - if _, ok := ref.(*ir.DebugRef); ok { - continue - } switch ref := ref.(type) { case *ir.Phi: walkRefs(*ref.Referrers()) - case *ir.Sigma: - walkRefs(*ref.Referrers()) case ir.Value: if !isAppend(ref) { isUsed = true @@ -195,8 +188,9 @@ func run(pass *analysis.Pass) (any, error) { seen2 := map[ir.Instruction]struct{}{} for k := range seen { - // the only values we allow are also instructions, so this type assertion cannot fail - seen2[k.(ir.Instruction)] = struct{}{} + if k, ok := k.(ir.Instruction); ok { + seen2[k] = struct{}{} + } } seen2[ins] = struct{}{} failed := false diff --git a/vendor/honnef.co/go/tools/staticcheck/sa4017/sa4017.go b/vendor/honnef.co/go/tools/staticcheck/sa4017/sa4017.go index 6177d17e..8302249f 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa4017/sa4017.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa4017/sa4017.go @@ -9,7 +9,6 @@ import ( "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" "honnef.co/go/tools/go/ir" - "honnef.co/go/tools/go/ir/irutil" "honnef.co/go/tools/go/types/typeutil" "honnef.co/go/tools/internal/passes/buildir" @@ -60,7 +59,7 @@ fnLoop: continue } refs := ins.Referrers() - if refs == nil || len(irutil.FilterDebug(*refs)) > 0 { + if refs == nil || len(*refs) > 0 { continue } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa4023/sa4023.go b/vendor/honnef.co/go/tools/staticcheck/sa4023/sa4023.go index 4ab13c83..3f1e866f 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa4023/sa4023.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa4023/sa4023.go @@ -7,7 +7,6 @@ import ( "honnef.co/go/tools/analysis/code" "honnef.co/go/tools/analysis/facts/nilness" - "honnef.co/go/tools/analysis/facts/typedness" "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" "honnef.co/go/tools/go/ir" @@ -23,7 +22,7 @@ var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Analyzer: &analysis.Analyzer{ Name: "SA4023", Run: run, - Requires: []*analysis.Analyzer{buildir.Analyzer, typedness.Analysis, nilness.Analysis}, + Requires: []*analysis.Analyzer{buildir.Analyzer, nilness.Analysis}, }, Doc: &lint.RawDocumentation{ Title: `Impossible comparison of interface value with untyped nil`, @@ -102,8 +101,7 @@ func run(pass *analysis.Pass) (any, error) { // known typed nils, or typed unknown nilness are being returned. irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR) - typedness := pass.ResultOf[typedness.Analysis].(*typedness.Result) - nilness := pass.ResultOf[nilness.Analysis].(*nilness.Result) + nilnessRes := pass.ResultOf[nilness.Analysis].(*nilness.Result) for _, fn := range irpkg.SrcFuncs { for _, b := range fn.Blocks { for _, instr := range b.Instrs { @@ -111,17 +109,16 @@ func run(pass *analysis.Pass) (any, error) { if !ok || !(binop.Op == token.EQL || binop.Op == token.NEQ) { continue } - if _, ok := binop.X.Type().Underlying().(*types.Interface); !ok || typeparams.IsTypeParam(binop.X.Type()) { + if !types.IsInterface(binop.X.Type()) || typeparams.IsTypeParam(binop.X.Type()) { // TODO support swapped X and Y continue } k, ok := binop.Y.(*ir.Const) if !ok || !k.IsNil() { - // if binop.X is an interface, then binop.Y can - // only be a Const if its untyped. A typed nil - // constant would first be passed to - // MakeInterface. + // if binop.X is an interface, then binop.Y can only be a + // Const if its untyped. A typed nil constant would first + // be passed to MakeInterface. continue } @@ -159,8 +156,9 @@ func run(pass *analysis.Pass) (any, error) { terms, err := typeparams.NormalTerms(x.X.Type()) if len(terms) == 0 || err != nil { - // Type is a type parameter with no type terms (or we couldn't determine the terms). Such a type - // _can_ be nil when put in an interface value. + // Type is a type parameter with no type terms (or we + // couldn't determine the terms). Such a type _can_ be + // nil when put in an interface value. continue } @@ -168,8 +166,11 @@ func run(pass *analysis.Pass) (any, error) { report.Report(pass, binop, fmt.Sprintf("this comparison is %s true", qualifier), report.Related(x.X, "the lhs of the comparison gets its value from here and has a concrete type")) } else { - // we can't generate related information for this, so make the diagnostic itself slightly more useful - report.Report(pass, binop, fmt.Sprintf("this comparison is %s true; the lhs of the comparison has been assigned a concretely typed value", qualifier)) + // we can't generate related information for this, so + // make the diagnostic itself slightly more useful + report.Report(pass, binop, + fmt.Sprintf("this comparison is %s true; the lhs of the comparison has been assigned a concretely typed value", + qualifier)) } continue } @@ -177,11 +178,13 @@ func run(pass *analysis.Pass) (any, error) { continue } - isNil, onlyGlobal := nilness.MayReturnNil(obj, idx) - if typedness.MustReturnTyped(obj, idx) && isNil && !onlyGlobal && !code.IsInTest(pass, binop) { - // Don't flag these comparisons in tests. Tests - // may be explicitly enforcing the invariant that - // a value isn't nil. + nillity := nilnessRes.Nilness(obj, idx) + if nillity.Outer == nilness.NeverNil && + !code.IsInTest(pass, binop) && + !irutil.IsTrivial(irpkg.Pkg.Prog.FuncValue(obj)) { + // Don't flag these comparisons in tests. Tests may be + // explicitly enforcing the invariant that a value isn't + // nil. var qualifier string switch binop.Op { @@ -194,8 +197,11 @@ func run(pass *analysis.Pass) (any, error) { } report.Report(pass, binop, fmt.Sprintf("this comparison is %s true", qualifier), // TODO support swapped X and Y - report.Related(binop.X, fmt.Sprintf("the lhs of the comparison is the %s return value of this function call", report.Ordinal(idx+1))), - report.Related(obj, fmt.Sprintf("%s never returns a nil interface value", typeutil.FuncName(obj)))) + report.Related(binop.X, + fmt.Sprintf("the lhs of the comparison is the %s return value of this function call", + report.Ordinal(idx+1))), + report.Related(obj, + fmt.Sprintf("%s never returns a nil interface value", typeutil.FuncName(obj)))) } } } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa4031/sa4031.go b/vendor/honnef.co/go/tools/staticcheck/sa4031/sa4031.go index e91e5ecd..3da5ba0b 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa4031/sa4031.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa4031/sa4031.go @@ -95,8 +95,6 @@ func run(pass *analysis.Pass) (any, error) { values = append(values, v) } return neverNil(v.X, false) - case *ir.Sigma: - return neverNil(v.X, true) case *ir.Phi: for _, e := range v.Edges { if !neverNil(e, true) { diff --git a/vendor/honnef.co/go/tools/staticcheck/sa5007/sa5007.go b/vendor/honnef.co/go/tools/staticcheck/sa5007/sa5007.go index d9a1dd7e..f0819c8a 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa5007/sa5007.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa5007/sa5007.go @@ -47,16 +47,11 @@ func run(pass *analysis.Pass) (any, error) { } block := site.Block() - for _, b := range fn.Blocks { + for b := range fn.Exits() { if block.Dominates(b) { continue } - if len(b.Instrs) == 0 { - continue - } - if _, ok := b.Control().(*ir.Return); ok { - return - } + return } report.Report(pass, site, "infinite recursive call") }) diff --git a/vendor/honnef.co/go/tools/staticcheck/sa5008/jsonv2.go b/vendor/honnef.co/go/tools/staticcheck/sa5008/jsonv2.go index be5339d5..6724ae1a 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa5008/jsonv2.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa5008/jsonv2.go @@ -141,7 +141,7 @@ func validateJSONTag(pass *analysis.Pass, field *ast.Field, tag string) { report.Report(pass, field.Tag, fmt.Sprintf("invalid appearance of unknown `case:%s` tag value", rawOpt)) } - case "inline": + case "embed", "inline": case "unknown": case "omitzero": case "omitempty": diff --git a/vendor/honnef.co/go/tools/staticcheck/sa5011/sa5011.go b/vendor/honnef.co/go/tools/staticcheck/sa5011/sa5011.go deleted file mode 100644 index 7b1b1333..00000000 --- a/vendor/honnef.co/go/tools/staticcheck/sa5011/sa5011.go +++ /dev/null @@ -1,221 +0,0 @@ -package sa5011 - -import ( - "go/types" - - "honnef.co/go/tools/analysis/lint" - "honnef.co/go/tools/analysis/report" - "honnef.co/go/tools/go/ir" - "honnef.co/go/tools/go/types/typeutil" - "honnef.co/go/tools/internal/passes/buildir" - - "golang.org/x/tools/go/analysis" -) - -var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ - Analyzer: &analysis.Analyzer{ - Name: "SA5011", - Run: run, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - Doc: &lint.RawDocumentation{ - Title: `Possible nil pointer dereference`, - - Text: `A pointer is being dereferenced unconditionally, while -also being checked against nil in another place. This suggests that -the pointer may be nil and dereferencing it may panic. This is -commonly a result of improperly ordered code or missing return -statements. Consider the following examples: - - func fn(x *int) { - fmt.Println(*x) - - // This nil check is equally important for the previous dereference - if x != nil { - foo(*x) - } - } - - func TestFoo(t *testing.T) { - x := compute() - if x == nil { - t.Errorf("nil pointer received") - } - - // t.Errorf does not abort the test, so if x is nil, the next line will panic. - foo(*x) - } - -Staticcheck tries to deduce which functions abort control flow. -For example, it is aware that a function will not continue -execution after a call to \'panic\' or \'log.Fatal\'. However, sometimes -this detection fails, in particular in the presence of -conditionals. Consider the following example: - - func Log(msg string, level int) { - fmt.Println(msg) - if level == levelFatal { - os.Exit(1) - } - } - - func Fatal(msg string) { - Log(msg, levelFatal) - } - - func fn(x *int) { - if x == nil { - Fatal("unexpected nil pointer") - } - fmt.Println(*x) - } - -Staticcheck will flag the dereference of \'x\', even though it is perfectly -safe. Staticcheck is not able to deduce that a call to -Fatal will exit the program. For the time being, the easiest -workaround is to modify the definition of Fatal like so: - - func Fatal(msg string) { - Log(msg, levelFatal) - panic("unreachable") - } - -We also hard-code functions from common logging packages such as -logrus. Please file an issue if we're missing support for a -popular package.`, - Since: "2020.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, -}) - -var Analyzer = SCAnalyzer.Analyzer - -func run(pass *analysis.Pass) (any, error) { - // This is an extremely trivial check that doesn't try to reason - // about control flow. That is, phis and sigmas do not propagate - // any information. As such, we can flag this: - // - // _ = *x - // if x == nil { return } - // - // but we cannot flag this: - // - // if x == nil { println(x) } - // _ = *x - // - // but we can flag this, because the if's body doesn't use x: - // - // if x == nil { println("this is bad") } - // _ = *x - // - // nor many other variations of conditional uses of or assignments to x. - // - // However, even this trivial implementation finds plenty of - // real-world bugs, such as dereference before nil pointer check, - // or using t.Error instead of t.Fatal when encountering nil - // pointers. - // - // On the flip side, our naive implementation avoids false positives in branches, such as - // - // if x != nil { _ = *x } - // - // due to the same lack of propagating information through sigma - // nodes. x inside the branch will be independent of the x in the - // nil pointer check. - // - // - // We could implement a more powerful check, but then we'd be - // getting false positives instead of false negatives because - // we're incapable of deducing relationships between variables. - // For example, a function might return a pointer and an error, - // and the error being nil guarantees that the pointer is not nil. - // Depending on the surrounding code, the pointer may still end up - // being checked against nil in one place, and guarded by a check - // on the error in another, which would lead to us marking some - // loads as unsafe. - // - // Unfortunately, simply hard-coding the relationship between - // return values wouldn't eliminate all false positives, either. - // Many other more subtle relationships exist. An abridged example - // from real code: - // - // if a == nil && b == nil { return } - // c := fn(a) - // if c != "" { _ = *a } - // - // where `fn` is guaranteed to return a non-empty string if a - // isn't nil. - // - // We choose to err on the side of false negatives. - - isNilConst := func(v ir.Value) bool { - if typeutil.IsPointerLike(v.Type()) { - if k, ok := v.(*ir.Const); ok { - return k.IsNil() - } - } - return false - } - - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - maybeNil := map[ir.Value]ir.Instruction{} - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - // Originally we looked at all ir.BinOp, but that would lead to calls like 'assert(x != nil)' causing false positives. - // Restrict ourselves to actual if statements, as these are more likely to affect control flow in a way we can observe. - if instr, ok := instr.(*ir.If); ok { - if cond, ok := instr.Cond.(*ir.BinOp); ok { - if isNilConst(cond.X) { - maybeNil[cond.Y] = cond - } - if isNilConst(cond.Y) { - maybeNil[cond.X] = cond - } - } - } - } - } - - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - var ptr ir.Value - switch instr := instr.(type) { - case *ir.Load: - ptr = instr.X - case *ir.Store: - ptr = instr.Addr - case *ir.IndexAddr: - ptr = instr.X - if typeutil.All(ptr.Type(), func(term *types.Term) bool { - if _, ok := term.Type().Underlying().(*types.Slice); ok { - return true - } - return false - }) { - // indexing a nil slice does not cause a nil pointer panic - // - // Note: This also works around the bad lowering of range loops over slices - // (https://github.com/dominikh/go-tools/issues/1053) - continue - } - case *ir.FieldAddr: - ptr = instr.X - } - if ptr != nil { - switch ptr.(type) { - case *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr: - // these cannot be nil - continue - } - if r, ok := maybeNil[ptr]; ok { - report.Report(pass, instr, "possible nil pointer dereference", - report.Related(r, "this check suggests that the pointer can be nil")) - } - } - } - } - } - - return nil, nil -} diff --git a/vendor/honnef.co/go/tools/staticcheck/sa5012/sa5012.go b/vendor/honnef.co/go/tools/staticcheck/sa5012/sa5012.go index 4c391198..51dc39fc 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa5012/sa5012.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa5012/sa5012.go @@ -12,6 +12,7 @@ import ( "honnef.co/go/tools/go/ir" "honnef.co/go/tools/go/ir/irutil" "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/iterutil" "honnef.co/go/tools/internal/passes/buildir" "golang.org/x/tools/go/analysis" @@ -140,7 +141,7 @@ func findSliceLenChecks(pass *analysis.Pass) { for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { for _, b := range fn.Blocks { // all paths go through this block - if !b.Dominates(fn.Exit) { + if !iterutil.All(fn.Returns(), b.Dominates) { continue } @@ -220,7 +221,7 @@ func findIndirectSliceLenChecks(pass *analysis.Pass) { for _, b := range fn.Blocks { // all paths go through this block - if !b.Dominates(fn.Exit) { + if !iterutil.All(fn.Returns(), b.Dominates) { continue } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa6001/sa6001.go b/vendor/honnef.co/go/tools/staticcheck/sa6001/sa6001.go index 9a1c86ab..c164967d 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa6001/sa6001.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa6001/sa6001.go @@ -85,25 +85,18 @@ func run(pass *analysis.Pass) (any, error) { continue } refs := ins.Referrers() - // need at least two (DebugRef) references: the - // conversion and the *ast.Ident - if refs == nil || len(*refs) < 2 { - continue - } - ident := false - // skip first reference, that's the conversion itself - for _, ref := range (*refs)[1:] { + anyUses := false + for _, ref := range *refs { switch ref := ref.(type) { - case *ir.DebugRef: - if _, ok := ref.Expr.(*ast.Ident); !ok { - // the string seems to be used somewhere - // unexpected; the default branch should - // catch this already, but be safe - continue insLoop + case *ir.MapLookup: + anyUses = true + if src, ok := ref.Source().(*ast.IndexExpr); ok { + if _, ok := src.Index.(*ast.Ident); !ok { + continue insLoop + } } else { - ident = true + continue insLoop } - case *ir.MapLookup: default: // the string is used somewhere else than a // map lookup @@ -111,9 +104,7 @@ func run(pass *analysis.Pass) (any, error) { } } - // the result of the conversion wasn't assigned to an - // identifier - if !ident { + if !anyUses { continue } report.Report(pass, ins, "m[string(key)] would be more efficient than k := string(key); m[k]") diff --git a/vendor/honnef.co/go/tools/staticcheck/sa6005/sa6005.go b/vendor/honnef.co/go/tools/staticcheck/sa6005/sa6005.go index 387875a7..05cfc1cd 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa6005/sa6005.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa6005/sa6005.go @@ -1,6 +1,7 @@ package sa6005 import ( + "fmt" "go/ast" "go/token" @@ -60,16 +61,18 @@ var ( func run(pass *analysis.Pass) (any, error) { for node, m := range code.Matches(pass, checkToLowerToUpperComparisonQ) { rn := pattern.NodeToAST(checkToLowerToUpperComparisonR.Root, m.State).(ast.Expr) + method := "strings.EqualFold" if m.State["tok"].(token.Token) == token.NEQ { rn = &ast.UnaryExpr{ Op: token.NOT, X: rn, } + method = "!" + method } report.Report(pass, node, - "should use strings.EqualFold instead", - report.Fixes(edit.Fix("Replace with strings.EqualFold", edit.ReplaceWithNode(pass.Fset, node, rn)))) + fmt.Sprintf("should use %s instead", method), + report.Fixes(edit.Fix("replace with "+method, edit.ReplaceWithNode(pass.Fset, node, rn)))) } return nil, nil } diff --git a/vendor/honnef.co/go/tools/staticcheck/sa9005/sa9005.go b/vendor/honnef.co/go/tools/staticcheck/sa9005/sa9005.go index 94c1f5dc..d367ecd6 100644 --- a/vendor/honnef.co/go/tools/staticcheck/sa9005/sa9005.go +++ b/vendor/honnef.co/go/tools/staticcheck/sa9005/sa9005.go @@ -50,7 +50,9 @@ var rules = map[string]callcheck.Check{ // // Also, should we flag gob? "encoding/json.Marshal": check(knowledge.Arg("json.Marshal.v"), "MarshalJSON", "MarshalText"), + "encoding/json.MarshalIndent": check(knowledge.Arg("json.MarshalIndent.v"), "MarshalJSON", "MarshalText"), "encoding/xml.Marshal": check(knowledge.Arg("xml.Marshal.v"), "MarshalXML", "MarshalText"), + "encoding/xml.MarshalIndent": check(knowledge.Arg("xml.MarshalIndent.v"), "MarshalXML", "MarshalText"), "(*encoding/json.Encoder).Encode": check(knowledge.Arg("(*encoding/json.Encoder).Encode.v"), "MarshalJSON", "MarshalText"), "(*encoding/xml.Encoder).Encode": check(knowledge.Arg("(*encoding/xml.Encoder).Encode.v"), "MarshalXML", "MarshalText"), diff --git a/vendor/honnef.co/go/tools/staticcheck/sa9010/sa9010.go b/vendor/honnef.co/go/tools/staticcheck/sa9010/sa9010.go new file mode 100644 index 00000000..c171ba1b --- /dev/null +++ b/vendor/honnef.co/go/tools/staticcheck/sa9010/sa9010.go @@ -0,0 +1,60 @@ +package sa9010 + +import ( + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA9010", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Returned function should be called in defer`, + Text: ` +If you have a function such as: + + func f() func() { + // Do something. + return func() { + // Do something. + } + } + +Then calling that in defer: + + defer f() + +Is almost always a mistake, since you typically want to call the returned +function: + + defer f()() +`, + Since: "2026.2", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (any, error) { + fn := func(n ast.Node) { + def := n.(*ast.DeferStmt) + if _, ok := pass.TypesInfo.TypeOf(def.Call).Underlying().(*types.Signature); ok { + report.Report(pass, def, "deferred return function not called") + } + } + + code.Preorder(pass, fn, (*ast.DeferStmt)(nil)) + return nil, nil +} diff --git a/vendor/honnef.co/go/tools/unused/implements.go b/vendor/honnef.co/go/tools/unused/implements.go index 1207e4a8..f7c37966 100644 --- a/vendor/honnef.co/go/tools/unused/implements.go +++ b/vendor/honnef.co/go/tools/unused/implements.go @@ -2,6 +2,8 @@ package unused import ( "go/types" + + "honnef.co/go/tools/go/types/typeutil" ) // lookupMethod returns the index of and method with matching package and name, or (-1, nil). @@ -61,7 +63,8 @@ func implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*type // A concrete type implements T if it implements all methods of T. var sels []*types.Selection - var c methodsChecker + + mapping := map[*types.TypeParam]types.Type{} for m := range T.Methods() { sel := msV.Lookup(m.Pkg(), m.Name()) if sel == nil { @@ -73,77 +76,27 @@ func implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*type return nil, false } - if !c.methodIsCompatible(f, m) { + if ok := typeutil.Unify(f.Type(), m.Type(), mapping); !ok { return nil, false } sels = append(sels, sel) } - return sels, true -} - -type methodsChecker struct { - typeParams map[*types.TypeParam]types.Type -} - -// Currently, this doesn't support methods like `foo(x []T)`. -func (c *methodsChecker) methodIsCompatible(implFunc *types.Func, interfaceFunc *types.Func) bool { - if types.Identical(implFunc.Type(), interfaceFunc.Type()) { - return true - } - implSig, implOk := implFunc.Type().(*types.Signature) - interfaceSig, interfaceOk := interfaceFunc.Type().(*types.Signature) - if !implOk || !interfaceOk { - // probably not reachable. handle conservatively. - return false - } - - if !c.typesAreCompatible(implSig.Params(), interfaceSig.Params()) { - return false - } - - if !c.typesAreCompatible(implSig.Results(), interfaceSig.Results()) { - return false - } - - return true -} - -func (c *methodsChecker) typesAreCompatible(implTypes, interfaceTypes *types.Tuple) bool { - if implTypes.Len() != interfaceTypes.Len() { - return false - } - for i := 0; i < implTypes.Len(); i++ { - if !c.typeIsCompatible(implTypes.At(i).Type(), interfaceTypes.At(i).Type()) { - return false + for tparam, targ := range mapping { + // This checks constraints on a best-effort basis, erring on the side + // of accepting too many types. + if !satisfiesConstraint(targ, tparam) { + return nil, false } } - return true + return sels, true } -func (c *methodsChecker) typeIsCompatible(implType, interfaceType types.Type) bool { - if types.Identical(implType, interfaceType) { - return true - } - // We only support trivial use of type parameters. This isn't fully compatible with compiler type checking yet. - tp, ok := interfaceType.(*types.TypeParam) - if !ok { - return false - } - if c.typeParams == nil { - c.typeParams = make(map[*types.TypeParam]types.Type) - } - if c.typeParams[tp] == nil { - if !satisfiesConstraint(implType, tp) { - return false - } - c.typeParams[tp] = implType +func satisfiesConstraint(t types.Type, tp *types.TypeParam) bool { + if t == nil { + // t is nil when we unify two type parameters. return true } - return types.Identical(c.typeParams[tp], implType) -} - -func satisfiesConstraint(t types.Type, tp *types.TypeParam) bool { bound := tp.Constraint().Underlying().(*types.Interface) return types.Satisfies(t, bound) } diff --git a/vendor/honnef.co/go/tools/unused/unused.go b/vendor/honnef.co/go/tools/unused/unused.go index 99060a35..fd7b5c04 100644 --- a/vendor/honnef.co/go/tools/unused/unused.go +++ b/vendor/honnef.co/go/tools/unused/unused.go @@ -748,6 +748,20 @@ func (g *graph) read(node ast.Node, by types.Object) { g.read(kv.Value, by) } } + if g.opts.FieldWritesAreUses && !unkeyed { + for _, elt := range node.Elts { + kv := elt.(*ast.KeyValueExpr) + fname := kv.Key.(*ast.Ident).Name + _, index, _ := types.LookupFieldOrMethod(typ, true, g.pkg, fname) + + cur := typ + for _, step := range index[:len(index)-1] { + field := cur.Field(step) + g.use(field, by) + cur = typeutil.CoreType(field.Type()).(*types.Struct) + } + } + } } else { for _, elt := range node.Elts { g.read(elt, by) @@ -1340,7 +1354,7 @@ func (g *graph) stmt(stmt ast.Stmt, by types.Object) { g.read(comm.Chan, by) g.read(comm.Value, by) case *ast.ExprStmt: - g.read(astutil.Unparen(comm.X).(*ast.UnaryExpr).X, by) + g.read(ast.Unparen(comm.X).(*ast.UnaryExpr).X, by) case *ast.AssignStmt: for _, lhs := range comm.Lhs { g.write(lhs, by) diff --git a/vendor/modules.txt b/vendor/modules.txt index 5c86dbfa..68eb0c88 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,9 +1,12 @@ -# 4d63.com/gocheckcompilerdirectives v1.3.0 +# 4d63.com/gocheckcompilerdirectives v1.4.0 ## explicit; go 1.22.0 4d63.com/gocheckcompilerdirectives/checkcompilerdirectives # 4d63.com/gochecknoglobals v0.2.2 ## explicit; go 1.18 4d63.com/gochecknoglobals/checknoglobals +# charm.land/lipgloss/v2 v2.0.6 +## explicit; go 1.25.0 +charm.land/lipgloss/v2 # codeberg.org/chavacava/garif v0.2.0 ## explicit; go 1.23 codeberg.org/chavacava/garif @@ -19,27 +22,35 @@ dev.gaijin.team/go/exhaustruct/v4/analyzer dev.gaijin.team/go/exhaustruct/v4/internal/comment dev.gaijin.team/go/exhaustruct/v4/internal/pattern dev.gaijin.team/go/exhaustruct/v4/internal/structure -# dev.gaijin.team/go/golib v0.6.0 +# dev.gaijin.team/go/exhaustruct/v5 v5.0.3 +## explicit; go 1.24.0 +dev.gaijin.team/go/exhaustruct/v5/analyzer +dev.gaijin.team/go/exhaustruct/v5/internal/astutil +dev.gaijin.team/go/exhaustruct/v5/internal/cache +dev.gaijin.team/go/exhaustruct/v5/internal/directive +dev.gaijin.team/go/exhaustruct/v5/internal/pattern +dev.gaijin.team/go/exhaustruct/v5/internal/structure +# dev.gaijin.team/go/golib v0.8.1 ## explicit; go 1.24 dev.gaijin.team/go/golib/e dev.gaijin.team/go/golib/fields # github.com/4meepo/tagalign v1.4.3 ## explicit; go 1.22.0 github.com/4meepo/tagalign -# github.com/Abirdcfly/dupword v0.1.7 -## explicit; go 1.24.0 +# github.com/Abirdcfly/dupword v0.1.8 +## explicit; go 1.25.0 github.com/Abirdcfly/dupword # github.com/AdminBenni/iota-mixing v1.0.0 ## explicit; go 1.23.3 github.com/AdminBenni/iota-mixing/pkg/analyzer github.com/AdminBenni/iota-mixing/pkg/analyzer/flags -# github.com/AlwxSin/noinlineerr v1.0.5 +# github.com/AlwxSin/noinlineerr v1.0.6 ## explicit; go 1.23.0 github.com/AlwxSin/noinlineerr -# github.com/Antonboom/errname v1.1.1 -## explicit; go 1.23.0 +# github.com/Antonboom/errname v1.1.2 +## explicit; go 1.24.0 github.com/Antonboom/errname/pkg/analyzer -# github.com/Antonboom/nilnil v1.1.1 +# github.com/Antonboom/nilnil v1.1.2 ## explicit; go 1.24.0 github.com/Antonboom/nilnil/pkg/analyzer # github.com/Antonboom/testifylint v1.6.4 @@ -58,10 +69,15 @@ github.com/Azure/go-ansiterm/winterm ## explicit; go 1.18 github.com/BurntSushi/toml github.com/BurntSushi/toml/internal +# github.com/ClickHouse/clickhouse-go-linter v1.2.1 +## explicit; go 1.24.0 +github.com/ClickHouse/clickhouse-go-linter/internal/util +github.com/ClickHouse/clickhouse-go-linter/passes/chbatchclose +github.com/ClickHouse/clickhouse-go-linter/passes/chrowserr # github.com/Djarvur/go-err113 v0.1.1 ## explicit; go 1.23.0 github.com/Djarvur/go-err113 -# github.com/Masterminds/semver/v3 v3.4.0 +# github.com/Masterminds/semver/v3 v3.5.0 ## explicit; go 1.21 github.com/Masterminds/semver/v3 # github.com/Microsoft/go-winio v0.6.2 @@ -81,8 +97,8 @@ github.com/MirrexOne/unqueryvet/pkg/config ## explicit; go 1.23.0 github.com/OpenPeeDeeP/depguard/v2 github.com/OpenPeeDeeP/depguard/v2/internal/utils -# github.com/alecthomas/chroma/v2 v2.23.1 -## explicit; go 1.22 +# github.com/alecthomas/chroma/v2 v2.27.0 +## explicit; go 1.25 github.com/alecthomas/chroma/v2 github.com/alecthomas/chroma/v2/formatters github.com/alecthomas/chroma/v2/formatters/html @@ -109,15 +125,12 @@ github.com/alingse/asasalint ## explicit; go 1.22.0 github.com/alingse/nilnesserr github.com/alingse/nilnesserr/internal/typeparams -# github.com/ashanbrown/forbidigo/v2 v2.3.0 +# github.com/ashanbrown/forbidigo/v2 v2.3.1 ## explicit; go 1.24.0 github.com/ashanbrown/forbidigo/v2/forbidigo -# github.com/ashanbrown/makezero/v2 v2.1.0 +# github.com/ashanbrown/makezero/v2 v2.2.1 ## explicit; go 1.24.0 github.com/ashanbrown/makezero/v2/makezero -# github.com/aymanbagabas/go-osc52/v2 v2.0.1 -## explicit; go 1.16 -github.com/aymanbagabas/go-osc52/v2 # github.com/beorn7/perks v1.0.1 ## explicit; go 1.11 github.com/beorn7/perks/quantile @@ -133,8 +146,8 @@ github.com/blizzy78/varnamelen # github.com/bombsimon/wsl/v4 v4.7.0 ## explicit; go 1.23 github.com/bombsimon/wsl/v4 -# github.com/bombsimon/wsl/v5 v5.6.0 -## explicit; go 1.24.0 +# github.com/bombsimon/wsl/v5 v5.9.0 +## explicit; go 1.26.0 github.com/bombsimon/wsl/v5 # github.com/breml/bidichk v0.3.3 ## explicit; go 1.23.0 @@ -142,13 +155,13 @@ github.com/breml/bidichk/pkg/bidichk # github.com/breml/errchkjson v0.4.1 ## explicit; go 1.23.0 github.com/breml/errchkjson -# github.com/butuzov/ireturn v0.4.0 -## explicit; go 1.23.0 +# github.com/butuzov/ireturn v0.4.1 +## explicit; go 1.25.0 github.com/butuzov/ireturn/analyzer github.com/butuzov/ireturn/analyzer/internal/config github.com/butuzov/ireturn/analyzer/internal/types -# github.com/butuzov/mirror v1.3.0 -## explicit; go 1.19 +# github.com/butuzov/mirror v1.3.3 +## explicit; go 1.25.0 github.com/butuzov/mirror github.com/butuzov/mirror/internal/checker # github.com/catenacyber/perfsprint v0.10.1 @@ -174,29 +187,33 @@ github.com/cespare/xxhash/v2 # github.com/charithe/durationcheck v0.0.11 ## explicit; go 1.16 github.com/charithe/durationcheck -# github.com/charmbracelet/colorprofile v0.3.1 -## explicit; go 1.23.0 +# github.com/charmbracelet/colorprofile v0.4.3 +## explicit; go 1.25.0 github.com/charmbracelet/colorprofile -# github.com/charmbracelet/lipgloss v1.1.0 -## explicit; go 1.18 -github.com/charmbracelet/lipgloss -# github.com/charmbracelet/x/ansi v0.10.1 -## explicit; go 1.23.0 +# github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886 +## explicit; go 1.25.0 +github.com/charmbracelet/ultraviolet +# github.com/charmbracelet/x/ansi v0.11.8 +## explicit; go 1.24.2 github.com/charmbracelet/x/ansi +github.com/charmbracelet/x/ansi/kitty github.com/charmbracelet/x/ansi/parser -# github.com/charmbracelet/x/cellbuf v0.0.13 -## explicit; go 1.18 -github.com/charmbracelet/x/cellbuf -# github.com/charmbracelet/x/term v0.2.1 -## explicit; go 1.18 +# github.com/charmbracelet/x/term v0.2.2 +## explicit; go 1.24.0 github.com/charmbracelet/x/term +# github.com/charmbracelet/x/termios v0.1.1 +## explicit; go 1.18 +github.com/charmbracelet/x/termios +# github.com/charmbracelet/x/windows v0.2.2 +## explicit; go 1.23.0 +github.com/charmbracelet/x/windows # github.com/ckaznocha/intrange v0.3.1 ## explicit; go 1.23.0 github.com/ckaznocha/intrange -# github.com/clipperhouse/displaywidth v0.10.0 +# github.com/clipperhouse/displaywidth v0.11.0 ## explicit; go 1.18 github.com/clipperhouse/displaywidth -# github.com/clipperhouse/uax29/v2 v2.6.0 +# github.com/clipperhouse/uax29/v2 v2.7.0 ## explicit; go 1.18 github.com/clipperhouse/uax29/v2/graphemes # github.com/containerd/errdefs v1.0.0 @@ -238,19 +255,17 @@ github.com/dave/dst/decorator/resolver github.com/dave/dst/decorator/resolver/gopackages github.com/dave/dst/decorator/resolver/gotypes github.com/dave/dst/dstutil -# github.com/davecgh/go-spew v1.1.1 -## explicit -github.com/davecgh/go-spew/spew # github.com/denis-tingaikin/go-header v0.5.0 ## explicit; go 1.21 github.com/denis-tingaikin/go-header # github.com/distribution/reference v0.6.0 ## explicit; go 1.20 github.com/distribution/reference -# github.com/dlclark/regexp2 v1.11.5 -## explicit; go 1.13 -github.com/dlclark/regexp2 -github.com/dlclark/regexp2/syntax +# github.com/dlclark/regexp2/v2 v2.2.1 +## explicit; go 1.25 +github.com/dlclark/regexp2/v2 +github.com/dlclark/regexp2/v2/helpers +github.com/dlclark/regexp2/v2/syntax # github.com/dnephin/pflag v1.0.7 ## explicit; go 1.12 github.com/dnephin/pflag @@ -261,7 +276,7 @@ github.com/docker/go-connections/tlsconfig # github.com/docker/go-units v0.5.0 ## explicit github.com/docker/go-units -# github.com/ebitengine/purego v0.10.1 +# github.com/ebitengine/purego v0.10.2 ## explicit; go 1.18 github.com/ebitengine/purego github.com/ebitengine/purego/internal/cgo @@ -280,8 +295,8 @@ github.com/fatih/structtag # github.com/felixge/httpsnoop v1.1.0 ## explicit; go 1.25 github.com/felixge/httpsnoop -# github.com/firefart/nonamedreturns v1.0.6 -## explicit; go 1.23.0 +# github.com/firefart/nonamedreturns v1.0.8 +## explicit; go 1.25.0 github.com/firefart/nonamedreturns/analyzer # github.com/fsnotify/fsnotify v1.9.0 ## explicit; go 1.17 @@ -290,10 +305,10 @@ github.com/fsnotify/fsnotify/internal # github.com/fzipp/gocyclo v0.6.0 ## explicit; go 1.18 github.com/fzipp/gocyclo -# github.com/ghostiam/protogetter v0.3.20 -## explicit; go 1.24.0 +# github.com/ghostiam/protogetter v0.3.21 +## explicit; go 1.25.0 github.com/ghostiam/protogetter -# github.com/go-critic/go-critic v0.14.3 +# github.com/go-critic/go-critic v0.14.4 ## explicit; go 1.24.0 github.com/go-critic/go-critic/checkers github.com/go-critic/go-critic/checkers/internal/astwalk @@ -373,7 +388,7 @@ github.com/gofrs/flock # github.com/golangci/asciicheck v0.5.0 ## explicit; go 1.23.0 github.com/golangci/asciicheck -# github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 +# github.com/golangci/dupl v0.0.0-20260401084720-c99c5cf5c202 ## explicit; go 1.22.0 github.com/golangci/dupl/job github.com/golangci/dupl/lib @@ -384,11 +399,12 @@ github.com/golangci/dupl/syntax/golang # github.com/golangci/go-printf-func-name v0.1.1 ## explicit; go 1.23.0 github.com/golangci/go-printf-func-name/pkg/analyzer -# github.com/golangci/gofmt v0.0.0-20250704145412-3e58ba0443c6 -## explicit; go 1.23.0 -github.com/golangci/gofmt/gofmt -# github.com/golangci/golangci-lint/v2 v2.11.4 -## explicit; go 1.25.0 +# github.com/golangci/gofmt v0.0.0-20260820135601-e84e05053792 +## explicit; go 1.26.0 +github.com/golangci/gofmt +github.com/golangci/gofmt/internal +# github.com/golangci/golangci-lint/v2 v2.13.1 +## explicit; go 1.26.0 github.com/golangci/golangci-lint/v2/cmd/golangci-lint github.com/golangci/golangci-lint/v2/internal/cache github.com/golangci/golangci-lint/v2/internal/errorutil @@ -400,6 +416,7 @@ github.com/golangci/golangci-lint/v2/internal/go/quoted github.com/golangci/golangci-lint/v2/internal/x/tools/diff github.com/golangci/golangci-lint/v2/internal/x/tools/diff/lcs github.com/golangci/golangci-lint/v2/internal/x/tools/driverutil +github.com/golangci/golangci-lint/v2/jsonschema github.com/golangci/golangci-lint/v2/pkg/commands github.com/golangci/golangci-lint/v2/pkg/commands/internal github.com/golangci/golangci-lint/v2/pkg/commands/internal/migrate @@ -432,6 +449,7 @@ github.com/golangci/golangci-lint/v2/pkg/golinters/asciicheck github.com/golangci/golangci-lint/v2/pkg/golinters/bidichk github.com/golangci/golangci-lint/v2/pkg/golinters/bodyclose github.com/golangci/golangci-lint/v2/pkg/golinters/canonicalheader +github.com/golangci/golangci-lint/v2/pkg/golinters/clickhouselint github.com/golangci/golangci-lint/v2/pkg/golinters/containedctx github.com/golangci/golangci-lint/v2/pkg/golinters/contextcheck github.com/golangci/golangci-lint/v2/pkg/golinters/copyloopvar @@ -572,6 +590,9 @@ github.com/golangci/plugin-module-register/register # github.com/golangci/revgrep v0.8.0 ## explicit; go 1.21 github.com/golangci/revgrep +# github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba +## explicit; go 1.25.0 +github.com/golangci/rowserrcheck/passes/rowserr # github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e ## explicit; go 1.23.0 github.com/golangci/swaggoswag @@ -610,7 +631,7 @@ github.com/gostaticanalysis/nilerr # github.com/hashicorp/go-immutable-radix/v2 v2.1.0 ## explicit; go 1.18 github.com/hashicorp/go-immutable-radix/v2 -# github.com/hashicorp/go-version v1.8.0 +# github.com/hashicorp/go-version v1.9.0 ## explicit github.com/hashicorp/go-version # github.com/hashicorp/golang-lru/v2 v2.0.7 @@ -653,12 +674,9 @@ github.com/jackc/pgx/v5/stdlib ## explicit; go 1.19 github.com/jackc/puddle/v2 github.com/jackc/puddle/v2/internal/genstack -# github.com/jgautheron/goconst v1.8.2 -## explicit; go 1.16 +# github.com/jgautheron/goconst v1.11.0 +## explicit; go 1.23 github.com/jgautheron/goconst -# github.com/jingyugao/rowserrcheck v1.1.1 -## explicit; go 1.13 -github.com/jingyugao/rowserrcheck/passes/rowserr # github.com/jjti/go-spancheck v0.6.5 ## explicit; go 1.22.1 github.com/jjti/go-spancheck @@ -668,8 +686,8 @@ github.com/julz/importas # github.com/karamaru-alpha/copyloopvar v1.2.2 ## explicit; go 1.24.0 github.com/karamaru-alpha/copyloopvar -# github.com/kisielk/errcheck v1.10.0 -## explicit; go 1.22.0 +# github.com/kisielk/errcheck v1.20.0 +## explicit; go 1.25.0 github.com/kisielk/errcheck/errcheck # github.com/kkHAIKE/contextcheck v1.1.6 ## explicit; go 1.23.0 @@ -696,8 +714,8 @@ github.com/lasiar/canonicalheader # github.com/ldez/exptostd v0.4.5 ## explicit; go 1.24.0 github.com/ldez/exptostd -# github.com/ldez/gomoddirectives v0.8.0 -## explicit; go 1.24.0 +# github.com/ldez/gomoddirectives v0.9.0 +## explicit; go 1.25.0 github.com/ldez/gomoddirectives # github.com/ldez/grignotin v0.10.1 ## explicit; go 1.24.0 @@ -720,7 +738,7 @@ github.com/leonklingele/grouper/pkg/analyzer/globals github.com/leonklingele/grouper/pkg/analyzer/imports github.com/leonklingele/grouper/pkg/analyzer/types github.com/leonklingele/grouper/pkg/analyzer/vars -# github.com/lucasb-eyer/go-colorful v1.2.0 +# github.com/lucasb-eyer/go-colorful v1.4.1 ## explicit; go 1.12 github.com/lucasb-eyer/go-colorful # github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e @@ -736,8 +754,8 @@ github.com/magiconair/properties ## explicit; go 1.23.0 github.com/manuelarte/embeddedstructfieldcheck/analyzer github.com/manuelarte/embeddedstructfieldcheck/internal -# github.com/manuelarte/funcorder v0.5.0 -## explicit; go 1.23.0 +# github.com/manuelarte/funcorder v0.6.0 +## explicit; go 1.25.0 github.com/manuelarte/funcorder/analyzer github.com/manuelarte/funcorder/internal # github.com/maratori/testableexamples v1.0.1 @@ -749,13 +767,13 @@ github.com/maratori/testpackage/pkg/testpackage # github.com/matoous/godox v1.1.0 ## explicit; go 1.18 github.com/matoous/godox -# github.com/mattn/go-colorable v0.1.14 +# github.com/mattn/go-colorable v0.1.15 ## explicit; go 1.18 github.com/mattn/go-colorable # github.com/mattn/go-isatty v0.0.20 ## explicit; go 1.15 github.com/mattn/go-isatty -# github.com/mattn/go-runewidth v0.0.19 +# github.com/mattn/go-runewidth v0.0.24 ## explicit; go 1.20 github.com/mattn/go-runewidth # github.com/mgechev/revive v1.15.0 @@ -832,9 +850,9 @@ github.com/moby/term/windows github.com/moricho/tparallel github.com/moricho/tparallel/pkg/ssafunc github.com/moricho/tparallel/pkg/ssainstr -# github.com/muesli/termenv v0.16.0 +# github.com/muesli/cancelreader v0.2.2 ## explicit; go 1.17 -github.com/muesli/termenv +github.com/muesli/cancelreader # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 ## explicit github.com/munnerz/goautoneg @@ -847,8 +865,8 @@ 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.23.0 -## explicit; go 1.25.0 +# github.com/nunnatsa/ginkgolinter v0.24.0 +## explicit; go 1.26.0 github.com/nunnatsa/ginkgolinter github.com/nunnatsa/ginkgolinter/config github.com/nunnatsa/ginkgolinter/internal/expression @@ -893,19 +911,15 @@ github.com/opencontainers/go-digest ## explicit; go 1.18 github.com/opencontainers/image-spec/specs-go github.com/opencontainers/image-spec/specs-go/v1 -# github.com/pelletier/go-toml/v2 v2.2.4 +# github.com/pelletier/go-toml/v2 v2.4.3 ## explicit; go 1.21.0 github.com/pelletier/go-toml/v2 -github.com/pelletier/go-toml/v2/internal/characters -github.com/pelletier/go-toml/v2/internal/danger +github.com/pelletier/go-toml/v2/internal/parserbridge github.com/pelletier/go-toml/v2/internal/tracker github.com/pelletier/go-toml/v2/unstable # github.com/pkg/errors v0.9.1 ## explicit github.com/pkg/errors -# github.com/pmezard/go-difflib v1.0.0 -## explicit -github.com/pmezard/go-difflib/difflib # github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 ## explicit; go 1.14 github.com/power-devops/perfstat @@ -959,14 +973,14 @@ github.com/quasilyte/regex/syntax # github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 ## explicit; go 1.17 github.com/quasilyte/stdinfo -# github.com/raeperd/recvcheck v0.2.0 -## explicit; go 1.22.0 +# github.com/raeperd/recvcheck v0.3.0 +## explicit; go 1.25.0 github.com/raeperd/recvcheck # github.com/rivo/uniseg v0.4.7 ## explicit; go 1.18 github.com/rivo/uniseg -# github.com/rogpeppe/go-internal v1.14.1 -## explicit; go 1.23 +# github.com/rogpeppe/go-internal v1.16.0 +## explicit; go 1.25 github.com/rogpeppe/go-internal/diff github.com/rogpeppe/go-internal/internal/syscall/windows github.com/rogpeppe/go-internal/internal/syscall/windows/sysdll @@ -976,6 +990,9 @@ github.com/rogpeppe/go-internal/robustio # github.com/ryancurrah/gomodguard v1.4.1 ## explicit; go 1.23.0 github.com/ryancurrah/gomodguard +# github.com/ryancurrah/gomodguard/v2 v2.1.3 +## explicit; go 1.25.0 +github.com/ryancurrah/gomodguard/v2 # github.com/ryanrolds/sqlclosecheck v0.6.0 ## explicit; go 1.25.0 github.com/ryanrolds/sqlclosecheck/pkg/analyzer @@ -985,7 +1002,7 @@ github.com/sagikazarmark/locafero # github.com/sanposhiho/wastedassign/v2 v2.1.0 ## explicit; go 1.18 github.com/sanposhiho/wastedassign/v2 -# github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 +# github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 ## explicit; go 1.21 github.com/santhosh-tekuri/jsonschema/v6 github.com/santhosh-tekuri/jsonschema/v6/kind @@ -996,8 +1013,8 @@ github.com/sashamelentyev/interfacebloat/pkg/analyzer ## explicit; go 1.23.0 github.com/sashamelentyev/usestdlibvars/pkg/analyzer github.com/sashamelentyev/usestdlibvars/pkg/analyzer/internal/mapping -# github.com/securego/gosec/v2 v2.24.8-0.20260309165252-619ce2117e08 -## explicit; go 1.25.0 +# github.com/securego/gosec/v2 v2.28.0 +## explicit; go 1.25.8 github.com/securego/gosec/v2 github.com/securego/gosec/v2/analyzers github.com/securego/gosec/v2/cwe @@ -1005,7 +1022,7 @@ github.com/securego/gosec/v2/internal/ssautil github.com/securego/gosec/v2/issue github.com/securego/gosec/v2/rules github.com/securego/gosec/v2/taint -# github.com/shirou/gopsutil/v4 v4.26.6 +# github.com/shirou/gopsutil/v4 v4.26.7 ## explicit; go 1.24.0 github.com/shirou/gopsutil/v4/common github.com/shirou/gopsutil/v4/cpu @@ -1013,8 +1030,8 @@ github.com/shirou/gopsutil/v4/internal/common github.com/shirou/gopsutil/v4/mem github.com/shirou/gopsutil/v4/net github.com/shirou/gopsutil/v4/process -# github.com/sirupsen/logrus v1.9.4 -## explicit; go 1.17 +# github.com/sirupsen/logrus v1.10.1 +## explicit; go 1.23 github.com/sirupsen/logrus # github.com/sivchari/containedctx v1.0.3 ## explicit; go 1.17 @@ -1028,8 +1045,8 @@ github.com/sourcegraph/conc github.com/sourcegraph/conc/internal/multierror github.com/sourcegraph/conc/iter github.com/sourcegraph/conc/panics -# github.com/sourcegraph/go-diff v0.7.0 -## explicit; go 1.14 +# github.com/sourcegraph/go-diff v0.8.0 +## explicit; go 1.20 github.com/sourcegraph/go-diff/diff # github.com/spf13/afero v1.15.0 ## explicit; go 1.23.0 @@ -1063,10 +1080,12 @@ github.com/stbenjam/no-sprintf-host-port/pkg/analyzer # github.com/stretchr/objx v0.5.3 ## explicit; go 1.20 github.com/stretchr/objx -# github.com/stretchr/testify v1.11.1 +# github.com/stretchr/testify v1.12.1 ## explicit; go 1.17 github.com/stretchr/testify/assert github.com/stretchr/testify/assert/yaml +github.com/stretchr/testify/internal/difflib +github.com/stretchr/testify/internal/spew github.com/stretchr/testify/mock github.com/stretchr/testify/require # github.com/subosito/gotenv v1.6.0 @@ -1089,11 +1108,11 @@ github.com/testcontainers/testcontainers-go/modules/cockroachdb # github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0 ## explicit; go 1.25.0 github.com/testcontainers/testcontainers-go/modules/postgres -# github.com/tetafro/godot v1.5.4 +# github.com/tetafro/godot v1.5.6 ## explicit; go 1.22 github.com/tetafro/godot -# github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 -## explicit; go 1.12 +# github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 +## explicit; go 1.22 github.com/timakin/bodyclose/passes/bodyclose # github.com/timonwong/loggercheck v0.11.0 ## explicit; go 1.22.0 @@ -1126,13 +1145,14 @@ github.com/ultraware/whitespace # github.com/uudashr/gocognit v1.2.1 ## explicit; go 1.24.0 github.com/uudashr/gocognit -# github.com/uudashr/iface v1.4.1 +# github.com/uudashr/iface v1.5.0 ## explicit; go 1.23.0 github.com/uudashr/iface/identical github.com/uudashr/iface/internal/directive github.com/uudashr/iface/opaque github.com/uudashr/iface/unexported github.com/uudashr/iface/unused +github.com/uudashr/iface/unusedmethod # github.com/xen0n/gosmopolitan v1.3.0 ## explicit; go 1.23.0 github.com/xen0n/gosmopolitan @@ -1159,14 +1179,14 @@ gitlab.com/bosi/decorder # go-simpler.org/musttag v0.14.0 ## explicit; go 1.23.0 go-simpler.org/musttag -# go-simpler.org/sloglint v0.11.1 -## explicit; go 1.23.0 +# go-simpler.org/sloglint v0.12.0 +## explicit; go 1.24.0 go-simpler.org/sloglint # go.augendre.info/arangolint v0.4.0 ## explicit; go 1.24.0 go.augendre.info/arangolint/pkg/analyzer -# go.augendre.info/fatcontext v0.9.0 -## explicit; go 1.23.0 +# go.augendre.info/fatcontext v0.10.0 +## explicit; go 1.25.0 go.augendre.info/fatcontext/pkg/analyzer # go.opentelemetry.io/auto/sdk v1.2.1 ## explicit; go 1.24.0 @@ -1217,7 +1237,7 @@ go.uber.org/zap/internal/exit go.uber.org/zap/internal/pool go.uber.org/zap/internal/stacktrace go.uber.org/zap/zapcore -# go.yaml.in/yaml/v3 v3.0.4 +# go.yaml.in/yaml/v3 v3.0.5 ## explicit; go 1.16 go.yaml.in/yaml/v3 # golang.org/x/crypto v0.54.0 @@ -1231,10 +1251,10 @@ golang.org/x/crypto/internal/alias golang.org/x/crypto/internal/poly1305 golang.org/x/crypto/ssh golang.org/x/crypto/ssh/internal/bcrypt_pbkdf -# golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 -## explicit; go 1.24.0 +# golang.org/x/exp/typeparams v0.0.0-20260811152304-ee035b5b010f +## explicit; go 1.25.0 golang.org/x/exp/typeparams -# golang.org/x/mod v0.37.0 +# golang.org/x/mod v0.40.0 ## explicit; go 1.25.0 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile @@ -1282,7 +1302,7 @@ golang.org/x/text/transform golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm golang.org/x/text/width -# golang.org/x/tools v0.47.0 +# golang.org/x/tools v0.49.0 ## explicit; go 1.25.0 golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis/passes/appends @@ -1308,7 +1328,9 @@ golang.org/x/tools/go/analysis/passes/hostport golang.org/x/tools/go/analysis/passes/httpmux golang.org/x/tools/go/analysis/passes/httpresponse golang.org/x/tools/go/analysis/passes/ifaceassert +golang.org/x/tools/go/analysis/passes/inline golang.org/x/tools/go/analysis/passes/inspect +golang.org/x/tools/go/analysis/passes/internal/gofixdirective golang.org/x/tools/go/analysis/passes/loopclosure golang.org/x/tools/go/analysis/passes/lostcancel golang.org/x/tools/go/analysis/passes/modernize @@ -1356,6 +1378,7 @@ golang.org/x/tools/internal/aliases golang.org/x/tools/internal/analysis/analyzerutil golang.org/x/tools/internal/analysis/typeindex golang.org/x/tools/internal/astutil +golang.org/x/tools/internal/astutil/free golang.org/x/tools/internal/event golang.org/x/tools/internal/event/core golang.org/x/tools/internal/event/keys @@ -1365,18 +1388,21 @@ golang.org/x/tools/internal/gcimporter golang.org/x/tools/internal/gocommand golang.org/x/tools/internal/gopathwalk golang.org/x/tools/internal/imports +golang.org/x/tools/internal/modindex golang.org/x/tools/internal/moreiters +golang.org/x/tools/internal/moremaps golang.org/x/tools/internal/packagepath golang.org/x/tools/internal/packagesinternal golang.org/x/tools/internal/pkgbits golang.org/x/tools/internal/refactor +golang.org/x/tools/internal/refactor/inline golang.org/x/tools/internal/stdlib golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typesinternal golang.org/x/tools/internal/typesinternal/typeindex golang.org/x/tools/internal/versions golang.org/x/tools/refactor/satisfy -# google.golang.org/protobuf v1.36.8 +# google.golang.org/protobuf v1.36.11 ## explicit; go 1.23 google.golang.org/protobuf/encoding/protodelim google.golang.org/protobuf/encoding/prototext @@ -1422,10 +1448,12 @@ gotest.tools/gotestsum/internal/filewatcher gotest.tools/gotestsum/internal/junitxml gotest.tools/gotestsum/internal/log gotest.tools/gotestsum/testjson -# honnef.co/go/tools v0.7.0 -## explicit; go 1.25.0 +# honnef.co/go/tools v0.8.0 +## explicit; go 1.26.0 honnef.co/go/tools/analysis/callcheck honnef.co/go/tools/analysis/code +honnef.co/go/tools/analysis/dfa +honnef.co/go/tools/analysis/dfa/dense honnef.co/go/tools/analysis/edit honnef.co/go/tools/analysis/facts/deprecated honnef.co/go/tools/analysis/facts/directives @@ -1433,7 +1461,6 @@ honnef.co/go/tools/analysis/facts/generated honnef.co/go/tools/analysis/facts/nilness honnef.co/go/tools/analysis/facts/purity honnef.co/go/tools/analysis/facts/tokenfile -honnef.co/go/tools/analysis/facts/typedness honnef.co/go/tools/analysis/lint honnef.co/go/tools/analysis/report honnef.co/go/tools/config @@ -1441,10 +1468,18 @@ honnef.co/go/tools/go/ast/astutil honnef.co/go/tools/go/ir honnef.co/go/tools/go/ir/irutil honnef.co/go/tools/go/types/typeutil -honnef.co/go/tools/internal/analysisinternal/typeindex +honnef.co/go/tools/internal/iterutil honnef.co/go/tools/internal/passes/buildir honnef.co/go/tools/internal/sharedcheck -honnef.co/go/tools/internal/typesinternal/typeindex +honnef.co/go/tools/internal/xtools-internal/aliases +honnef.co/go/tools/internal/xtools-internal/analysis/typeindex +honnef.co/go/tools/internal/xtools-internal/astutil +honnef.co/go/tools/internal/xtools-internal/graph +honnef.co/go/tools/internal/xtools-internal/moreiters +honnef.co/go/tools/internal/xtools-internal/typeparams +honnef.co/go/tools/internal/xtools-internal/typesinternal +honnef.co/go/tools/internal/xtools-internal/typesinternal/typeindex +honnef.co/go/tools/internal/xtools-internal/versions honnef.co/go/tools/knowledge honnef.co/go/tools/pattern honnef.co/go/tools/printf @@ -1579,7 +1614,6 @@ honnef.co/go/tools/staticcheck/sa5007 honnef.co/go/tools/staticcheck/sa5008 honnef.co/go/tools/staticcheck/sa5009 honnef.co/go/tools/staticcheck/sa5010 -honnef.co/go/tools/staticcheck/sa5011 honnef.co/go/tools/staticcheck/sa5012 honnef.co/go/tools/staticcheck/sa6000 honnef.co/go/tools/staticcheck/sa6001 @@ -1596,6 +1630,7 @@ honnef.co/go/tools/staticcheck/sa9006 honnef.co/go/tools/staticcheck/sa9007 honnef.co/go/tools/staticcheck/sa9008 honnef.co/go/tools/staticcheck/sa9009 +honnef.co/go/tools/staticcheck/sa9010 honnef.co/go/tools/stylecheck honnef.co/go/tools/stylecheck/st1000 honnef.co/go/tools/stylecheck/st1001 @@ -1616,13 +1651,13 @@ honnef.co/go/tools/stylecheck/st1021 honnef.co/go/tools/stylecheck/st1022 honnef.co/go/tools/stylecheck/st1023 honnef.co/go/tools/unused -# mvdan.cc/gofumpt v0.9.2 -## explicit; go 1.24.0 +# mvdan.cc/gofumpt v0.11.0 +## explicit; go 1.25.0 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/unparam v0.0.0-20251027182757-5beb8c8f8f15 -## explicit; go 1.24.0 +# mvdan.cc/unparam v0.0.0-20260818115549-3f964bcb5673 +## explicit; go 1.25.0 mvdan.cc/unparam/check diff --git a/vendor/mvdan.cc/gofumpt/format/format.go b/vendor/mvdan.cc/gofumpt/format/format.go index 879969da..438a73b5 100644 --- a/vendor/mvdan.cc/gofumpt/format/format.go +++ b/vendor/mvdan.cc/gofumpt/format/format.go @@ -23,7 +23,6 @@ import ( "unicode" "unicode/utf8" - "github.com/google/go-cmp/cmp" "golang.org/x/tools/go/ast/astutil" "mvdan.cc/gofumpt/internal/govendor/go/format" @@ -57,11 +56,81 @@ type Options struct { // is formatted as if it weren't inside a module. ModulePath string - // ExtraRules enables extra formatting rules, such as grouping function + // ExtraRules enables all extra formatting rules, such as grouping function // parameters with repeated types together. + // + // Deprecated: use [Options.Extra] instead. ExtraRules bool + + // Extra allows enabling extra formatting rules which are disabled by default. + Extra Extra +} + +// Extra is the set of extra formatting rules which are available. +// +// As the formatter evolves, we might add or remove boolean fields here. +// Go API users who wish to avoid build errors in such cases +// can use the string API in [Extra.Set]. +type Extra struct { + // TODO: should we have "All" to turn them all on, + // akin to how the CLI has -extra=true for historical reasons? + // I lean against it, as it should be a conscious choice to turn on + // each of these extra rules, and we should be able to add more rules + // without fear of causing unexpected changes for users. + + // GroupParams groups function parameters with repeated types. + GroupParams bool + + // ClotheReturns clothes naked returns in functions with named results. + ClotheReturns bool + + // BalanceCalls places a multi-line call's closing parenthesis on its + // own line when the opening parenthesis ends a line. + BalanceCalls bool +} + +func (e *Extra) String() string { + var active []string + if e.GroupParams { + active = append(active, "group_params") + } + if e.ClotheReturns { + active = append(active, "clothe_returns") + } + if e.BalanceCalls { + active = append(active, "balance_calls") + } + return strings.Join(active, ",") +} + +func (e *Extra) Set(v string) error { + if v == "true" { + e.GroupParams = true + e.ClotheReturns = true + e.BalanceCalls = true + return nil + } + *e = Extra{} + if v == "false" { + return nil + } + for s := range strings.SplitSeq(v, ",") { + switch s { + case "group_params": + e.GroupParams = true + case "clothe_returns": + e.ClotheReturns = true + case "balance_calls": + e.BalanceCalls = true + default: + return fmt.Errorf("unknown rule: %q", s) + } + } + return nil } +func (e *Extra) IsBoolFlag() bool { return true } + // Source formats src in gofumpt's format, assuming that src holds a valid Go // source file. func Source(src []byte, opts Options) ([]byte, error) { @@ -91,6 +160,10 @@ func Source(src []byte, opts Options) ([]byte, error) { func File(fset *token.FileSet, file *ast.File, opts Options) { simplify(file) + if opts.ExtraRules { + opts.Extra.Set("true") // enable all the extra rules + } + if opts.LangVersion == "" { opts.LangVersion = "go1" } else { @@ -258,6 +331,37 @@ func (f *fumpter) removeLinesBetween(from, to token.Pos) { f.removeLines(f.Line(from)+1, f.Line(to)) } +// removeParens unwraps a single-spec var group like "var (\n\tx = 1\n)" into a +// lone "var x = 1". It only acts on such groups without a doc comment. +func (f *fumpter) removeParens(node *ast.GenDecl) { + if node.Tok != token.VAR || len(node.Specs) != 1 || + !node.Lparen.IsValid() || node.Doc != nil { + return + } + specPos := node.Specs[0].Pos() + specEnd := node.Specs[0].End() + + if len(f.commentsBetween(node.TokPos, specPos)) > 0 { + // 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)) + } + 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. + node.Lparen = token.NoPos + node.Rparen = token.NoPos +} + func (f *fumpter) Position(p token.Pos) token.Position { return f.file.PositionFor(p, false) } @@ -337,11 +441,68 @@ var rxCommentDirective = regexp.MustCompile( `|sys(?:nb)?\b` + `)`) +// rxShebangComment matches a shebang like `//usr/bin/env go run`. +var rxShebangComment = regexp.MustCompile(`^//[^ /].*\bbin/`) + +// commentGroupLooksLikeCode reports whether the lines of a //-style comment +// group parse as Go statements with at least one non-trivial statement. +// A bare identifier path or label is treated as trivial, since prose like +// "// foo" or "// TODO: bar" parses but is not commented-out code. +func commentGroupLooksLikeCode(group *ast.CommentGroup) bool { + src := "package p\nfunc _() {\n" + group.Text() + "}\n" + // AllErrors avoids the parser's panic/recover bailout on too many errors, + // which crashes under tinygo's Wasm target as it lacks recover support. + file, err := parser.ParseFile(token.NewFileSet(), "", src, parser.SkipObjectResolution|parser.AllErrors) + if err != nil { + return false + } + fn, _ := file.Decls[0].(*ast.FuncDecl) + if fn == nil || fn.Body == nil { + return false + } + for _, stmt := range fn.Body.List { + if !isTrivialStmt(stmt) { + return true + } + } + return false +} + +func isTrivialStmt(stmt ast.Stmt) bool { + switch s := stmt.(type) { + case *ast.ExprStmt: + return isIdentPath(s.X) + case *ast.LabeledStmt: + return isTrivialStmt(s.Stmt) + case *ast.EmptyStmt: + return true + } + return false +} + +func isIdentPath(expr ast.Expr) bool { + switch e := expr.(type) { + case *ast.Ident: + return true + case *ast.SelectorExpr: + return isIdentPath(e.X) + } + return false +} + func (f *fumpter) applyPre(c *astutil.Cursor) { f.splitLongLine(c) switch node := c.Node().(type) { case *ast.File: + // Unwrap single-spec var groups before the joining below, + // so an adjacent var line and var group merge in one pass. + for _, decl := range node.Decls { + if decl, ok := decl.(*ast.GenDecl); ok { + f.removeParens(decl) + } + } + // Join contiguous lone var/const/import lines. // Abort if there are empty lines in between, // including a leading comment if it's a directive. @@ -354,6 +515,7 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { continue } lastPos := start.Pos() + merged := false contLoop: for i++; i < len(node.Decls); { cont, ok := node.Decls[i].(*ast.GenDecl) @@ -377,17 +539,25 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { } start.Specs = append(start.Specs, cont.Specs...) + merged = true + end := cont.End() if c := f.inlineComment(cont.End()); c != nil { // don't move an inline comment outside - start.Rparen = c.End() - } else { - // so the code below treats the joined - // decl group as multi-line - start.Rparen = cont.End() + end = c.End() } + // Point Rparen at the last content character, like a real + // ')', so start.End() stays on the content's final line and + // the empty-line separator below is idempotent in one pass. + start.Rparen = end - 1 lastPos = cont.Pos() i++ } + // Re-sort imports in the new group so the output is idempotent. + // Set Lparen so ast.SortImports doesn't skip the merged decl. + if merged && start.Tok == token.IMPORT { + start.Lparen = start.TokPos + token.Pos(len("import")) + ast.SortImports(f.fset, f.astFile) + } } node.Decls = newDecls @@ -399,15 +569,30 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { var lastEnd token.Pos for _, decl := range node.Decls { pos := decl.Pos() - comments := f.commentsBetween(lastEnd, pos) - if len(comments) > 0 { - pos = comments[0].Pos() + // Trailing inline comments on lastEnd's line belong to the + // previous decl and extend its effective end. + effectiveEnd := lastEnd + lastEndLine := f.Line(lastEnd) + for _, cg := range f.commentsBetween(lastEnd, pos) { + if f.Line(cg.Pos()) != lastEndLine { + pos = cg.Pos() + break + } + effectiveEnd = cg.End() } // Note that we want End-1, as End is the character after the node. multi := f.Line(pos) < f.Line(decl.End()-1) - if multi && lastMulti && f.Line(lastEnd)+1 == f.Line(pos) { - f.addNewline(lastEnd) + // A func declaration which fits on a single source line may + // still be printed across multiple lines: go/printer's funcBody + // breaks the body onto its own lines once header+body exceeds + // 100 bytes. Approximate that with the source byte length. + if fn, _ := decl.(*ast.FuncDecl); fn != nil && !multi && fn.Body != nil && + f.Offset(fn.End())-f.Offset(fn.Pos()) > 100 { + multi = true + } + if multi && lastMulti && f.Line(effectiveEnd)+1 == f.Line(pos) { + f.addNewline(effectiveEnd) } lastMulti = multi @@ -418,6 +603,10 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { groupLoop: for _, group := range node.Comments { for _, comment := range group.List { + // Leave shebang lines like `//usr/bin/env go run` alone. + if f.Line(comment.Slash) == 1 && rxShebangComment.MatchString(comment.Text) { + continue groupLoop + } if comment.Text == "//gofumpt:diagnose" || strings.HasPrefix(comment.Text, "//gofumpt:diagnose ") { slc := []string{ "//gofumpt:diagnose", @@ -427,8 +616,8 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { "-lang=" + f.LangVersion, "-modpath=" + f.ModulePath, } - if f.ExtraRules { - slc = append(slc, "-extra") + if s := f.Extra.String(); s != "" { + slc = append(slc, "-extra="+s) } comment.Text = strings.Join(slc, " ") } @@ -447,6 +636,9 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { continue groupLoop } } + if commentGroupLooksLikeCode(group) { + continue groupLoop + } // If none of the comment group's lines look like a // directive or code, add spaces, if needed. for _, comment := range group.List { @@ -488,31 +680,7 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { // Single var declarations shouldn't use parentheses, unless // there's a comment on the grouped declaration. - if node.Tok == token.VAR && len(node.Specs) == 1 && - node.Lparen.IsValid() && node.Doc == nil { - specPos := node.Specs[0].Pos() - specEnd := node.Specs[0].End() - - if len(f.commentsBetween(node.TokPos, specPos)) > 0 { - // 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)) - } - 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. - node.Lparen = token.NoPos - node.Rparen = token.NoPos - } + f.removeParens(node) case *ast.InterfaceType: if len(node.Methods.List) > 0 { @@ -682,8 +850,7 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { f.removeLinesBetween(bodyEnd, node.End()) } - // Merging adjacent fields (e.g. parameters) is disabled by default. - if !f.ExtraRules { + if !f.Extra.GroupParams { break } switch c.Parent().(type) { @@ -694,6 +861,14 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { // Do not merge adjacent fields in structs. } + case *ast.ParenExpr: + // Unwrap any chain of redundant inner parens first, + // since astutil.Apply does not walk replacement nodes. + node.X = ast.Unparen(node.X) + if f.canRemoveParens(node) { + c.Replace(node.X) + } + case *ast.BasicLit: // Octal number literals were introduced in Go 1.13. if goversion.Compare(f.LangVersion, "go1.13") >= 0 { @@ -704,15 +879,21 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { } case *ast.AssignStmt: - // Only remove lines between the assignment token and the first right-hand side expression - f.removeLines(f.Line(node.TokPos), f.Line(node.Rhs[0].Pos())) + // Only remove lines between the assignment token and the right-hand side + // for simple single-value assignments. Skip multi-value assignments and + // binary expressions like long string concatenations, where a line break + // after the assignment token can improve readability. + if len(node.Rhs) == 1 { + if _, ok := node.Rhs[0].(*ast.BinaryExpr); !ok { + f.removeLines(f.Line(node.TokPos), f.Line(node.Rhs[0].Pos())) + } + } case *ast.ReturnStmt: if len(node.Results) > 0 { break } - // Clothing naked returns is disabled by default. - if !f.ExtraRules { + if !f.Extra.ClotheReturns { break } results := f.parentFuncTypes[len(f.parentFuncTypes)-1].Results @@ -822,6 +1003,33 @@ func (f *fumpter) applyPost(c *astutil.Cursor) { f.addNewline(elem1.End()) } } + + // In a multi-line call, if the opening parenthesis is at the end of a + // line, the closing parenthesis should be at the start of a line. + // See https://github.com/mvdan/gofumpt/issues/74. + case *ast.CallExpr: + if !f.Extra.BalanceCalls { + break + } + if len(node.Args) == 0 { + break + } + openLine := f.Line(node.Lparen) + closeLine := f.Line(node.Rparen) + if openLine == closeLine { + break + } + firstLine := f.Line(node.Args[0].Pos()) + lastEnd := node.Args[len(node.Args)-1].End() + if comment := f.inlineComment(lastEnd); comment != nil { + lastEnd = comment.End() + } + lastLine := f.Line(lastEnd) + openAtEOL := openLine != firstLine + closeAtBOL := closeLine != lastLine + if openAtEOL && !closeAtBOL { + f.addNewline(node.Rparen) + } } } @@ -908,6 +1116,46 @@ func (f *fumpter) splitLongLine(c *astutil.Cursor) { } } +// canRemoveParens reports whether the parentheses around node are definitely +// useless and can be safely removed without changing intent. +func (f *fumpter) canRemoveParens(node *ast.ParenExpr) bool { + // Don't drop parens which contain comments, + // as the printer may not place them well without the parens. + if len(f.commentsBetween(node.Lparen, node.Rparen)) > 0 { + return false + } + return !keepParens(node.X, true) +} + +// keepParens reports whether the parentheses directly around expr should be +// kept: around binary, unary, and type expressions for readability and for +// conversions like `(<-chan T)(v)`, but only when outermost; and around an +// expression whose leftmost operand is a composite literal, whose brace would +// otherwise open an if, for, or switch body. +func keepParens(expr ast.Expr, outermost bool) bool { + switch expr := expr.(type) { + case *ast.CompositeLit: + return true + case *ast.CallExpr: + return keepParens(expr.Fun, false) + case *ast.SelectorExpr: + return keepParens(expr.X, false) + case *ast.IndexExpr: + return keepParens(expr.X, false) + case *ast.IndexListExpr: + return keepParens(expr.X, false) + case *ast.SliceExpr: + return keepParens(expr.X, false) + case *ast.TypeAssertExpr: + return keepParens(expr.X, false) + case *ast.BinaryExpr, *ast.UnaryExpr, *ast.StarExpr, + *ast.ChanType, *ast.ArrayType, *ast.MapType, + *ast.FuncType, *ast.InterfaceType, *ast.StructType: + return outermost + } + return false +} + func isComposite(node ast.Node) *ast.CompositeLit { switch node := node.(type) { case *ast.CompositeLit: @@ -1093,16 +1341,28 @@ func (f *fumpter) shouldMergeAdjacentFields(f1, f2 *ast.Field) bool { // Only merge if the types that the syntax nodes represent are equal, // e.g. two *ast.Ident nodes "int" are equal, but the two *ast.Ident nodes - // "string" and "bool" are not. Hence we use go-cmp to do deep comparisons - // while ignoring position information, as it is irrelevant. + // "string" and "bool" are not. We use reflection to quickly discard most cases. + // + // We use an empty [token.FileSet] so that positions are ignored when printing, + // and two syntax nodes with different uses of newlines end up the same. // // Note that we could in theory use go/types here, but in practice gofumpt // needs to be fast, hence it shouldn't rely on expensive typechecking. - opt := cmp.Comparer(func(x, y token.Pos) bool { return true }) - return cmp.Equal(f1.Type, f2.Type, opt) + if reflect.TypeOf(f1.Type) != reflect.TypeOf(f2.Type) { + return false + } + emptyFset := token.NewFileSet() + var b1, b2 bytes.Buffer + if err := format.Node(&b1, emptyFset, f1.Type); err != nil { + return false + } + if err := format.Node(&b2, emptyFset, f2.Type); err != nil { + return false + } + return bytes.Equal(b1.Bytes(), b2.Bytes()) } -var posType = reflect.TypeOf(token.NoPos) +var posType = reflect.TypeFor[token.Pos]() // setPos recursively sets all position fields in the node v to pos. func setPos(v reflect.Value, pos token.Pos) { diff --git a/vendor/mvdan.cc/gofumpt/format/rewrite.go b/vendor/mvdan.cc/gofumpt/format/rewrite.go index ec7a2e5d..47ff5ee7 100644 --- a/vendor/mvdan.cc/gofumpt/format/rewrite.go +++ b/vendor/mvdan.cc/gofumpt/format/rewrite.go @@ -2,6 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// NOTE(gofumpt): the original cmd/gofmt/rewrite.go is mostly stripped here. +// gofumpt drops the -r flag (use `gofmt -r` instead), so the rewrite engine +// (initRewrite, parseExpr, rewriteFile, apply, set, subst) and its +// reflect-helpers (objectPtrNil, scopePtrNil, scopePtrType) are gone. Only +// match/isWildcard remain because simplify.go still uses them to compare +// AST literals when omitting redundant types in composite literals. + package format import ( @@ -14,10 +21,10 @@ import ( // Values/types for special cases. var ( - identType = reflect.TypeOf((*ast.Ident)(nil)) - objectPtrType = reflect.TypeOf((*ast.Object)(nil)) - positionType = reflect.TypeOf(token.NoPos) - callExprType = reflect.TypeOf((*ast.CallExpr)(nil)) + identType = reflect.TypeFor[*ast.Ident]() + objectPtrType = reflect.TypeFor[*ast.Object]() + positionType = reflect.TypeFor[token.Pos]() + callExprType = reflect.TypeFor[*ast.CallExpr]() ) func isWildcard(s string) bool { diff --git a/vendor/mvdan.cc/gofumpt/format/simplify.go b/vendor/mvdan.cc/gofumpt/format/simplify.go index 11764646..363f8d05 100644 --- a/vendor/mvdan.cc/gofumpt/format/simplify.go +++ b/vendor/mvdan.cc/gofumpt/format/simplify.go @@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// NOTE(gofumpt): moved into the format package (from package main) so that +// syntax simplification is exported via the Go API. gofumpt always simplifies, +// so the -s flag was dropped; see also the removal of the -r rewrite logic in +// rewrite.go, which left this file as the sole user of match/isWildcard. + package format import ( diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/internal.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/internal.go index 383655f1..df035871 100644 --- a/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/internal.go +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/internal.go @@ -33,7 +33,7 @@ func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( // 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 file, sourceAdj, indentAdj, err + return } // If this is a declaration list, make it a source file @@ -49,13 +49,13 @@ func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( src = src[indent+len("package p\n"):] return bytes.TrimSpace(src) } - return file, sourceAdj, indentAdj, err + 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 file, sourceAdj, indentAdj, err + return } // If this is a statement list, make it a source file @@ -86,7 +86,7 @@ func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( } // Succeeded, or out of options. - return file, sourceAdj, indentAdj, err + return } // format formats the given package file originally obtained from src diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go index df3b7250..c7d2b0f1 100644 --- a/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go @@ -57,7 +57,7 @@ func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (nbre p.print(newline) } } - return nbreaks + return } // setComment sets g as the next comment if g != nil and if node comments @@ -465,7 +465,7 @@ func identListSize(list []*ast.Ident, maxSize int) (size int) { break } } - return size + return } func (p *printer) isOneLineFieldList(list []*ast.Field) bool { @@ -693,7 +693,7 @@ func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) { maxProblem = max(maxProblem, 4) } } - return has4, has5, maxProblem + return } func cutoff(e *ast.BinaryExpr, depth int) int { @@ -1818,14 +1818,14 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { cfg := Config{Mode: RawFormat} var counter sizeCounter if err := cfg.fprint(&counter, p.fset, n, p.nodeSizes); err != nil { - return size + return } if counter.size <= maxSize && !counter.hasNewline { // n fits in a single line size = counter.size p.nodeSizes[n] = size } - return size + return } // numLines returns the number of lines spanned by node n in the original source. @@ -1959,7 +1959,7 @@ func declToken(decl ast.Decl) (tok token.Token) { case *ast.FuncDecl: tok = token.FUNC } - return tok + return } func (p *printer) declList(list []ast.Decl) { diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go index 00713309..a6c74c72 100644 --- a/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go @@ -715,7 +715,7 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, dropped wroteNewline = true } - return wroteNewline, droppedFF + return } // containsLinebreak reports whether the whitespace buffer contains any line breaks. @@ -809,7 +809,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro // 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 wroteNewline, droppedFF + return } // writeWhitespace writes the first n whitespace entries. @@ -878,7 +878,7 @@ func mayCombine(prev token.Token, next byte) (b bool) { case token.AND: b = next == '&' || next == '^' // && or &^ } - return b + return } func (p *printer) setPos(pos token.Pos) { @@ -1041,7 +1041,7 @@ func (p *printer) flush(next token.Position, tok token.Token) (wroteNewline, dro // otherwise, write any leftover whitespace p.writeWhitespace(len(p.wsbuf)) } - return wroteNewline, droppedFF + return } // getDoc returns the ast.CommentGroup associated with n, if any. @@ -1269,7 +1269,7 @@ func (p *trimmer) Write(data []byte) (n int, err error) { panic("unreachable") } if err != nil { - return n, err + return } } n = len(data) @@ -1280,7 +1280,7 @@ func (p *trimmer) Write(data []byte) (n int, err error) { p.resetSpace() } - return n, err + return } // ---------------------------------------------------------------------------- @@ -1361,7 +1361,7 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node any, nodeS p := newPrinter(cfg, fset, nodeSizes) defer p.free() if err = p.printNode(node); err != nil { - return err + return } // print outstanding comments p.impliedSemi = false // EOF acts like a newline @@ -1397,7 +1397,7 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node any, nodeS // write printer result via tabwriter/trimmer to output if _, err = output.Write(p.output); err != nil { - return err + return } // flush tabwriter, if any @@ -1405,7 +1405,7 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node any, nodeS err = tw.Flush() } - return err + return } // A CommentedNode bundles an AST node and corresponding comments. diff --git a/vendor/mvdan.cc/unparam/check/check.go b/vendor/mvdan.cc/unparam/check/check.go index 7ca02c29..6bd6fdb6 100644 --- a/vendor/mvdan.cc/unparam/check/check.go +++ b/vendor/mvdan.cc/unparam/check/check.go @@ -73,6 +73,11 @@ type Checker struct { // to typecheck properly, as they're required to implement interfaces. typesImplementing map[*types.Named][]string + // linknamed records the funcs with a go:linkname directive in their + // doc comment, keyed by the position of the func's name, as their + // signatures cannot change. + linknamed map[token.Pos]bool + // localCallSites is a very simple form of a callgraph, only recording // direct function calls within a single package. localCallSites map[*ssa.Function][]ssa.CallInstruction @@ -100,6 +105,9 @@ func (c *Checker) lines(args ...string) ([]string, error) { if packages.PrintErrors(pkgs) > 0 { return nil, fmt.Errorf("encountered errors") } + if len(pkgs) == 0 { + return nil, fmt.Errorf("no packages to check") + } prog, _ := ssautil.Packages(pkgs, 0) prog.Build() @@ -113,7 +121,7 @@ func (c *Checker) lines(args ...string) ([]string, error) { prevLine := "" for _, issue := range issues { fpos := prog.Fset.Position(issue.Pos()).String() - if strings.HasPrefix(fpos, c.wd) { + if strings.HasPrefix(fpos, c.wd+string(filepath.Separator)) { fpos = fpos[len(c.wd)+1:] } line := fmt.Sprintf("%s: %s", fpos, issue.Message()) @@ -167,12 +175,25 @@ func generatedDoc(text string) bool { strings.Contains(text, "DO NOT EDIT") } +// linknameDoc reports whether a doc comment contains a go:linkname directive. +func linknameDoc(doc *ast.CommentGroup) bool { + if doc == nil { + return false + } + for _, comment := range doc.List { + if strings.HasPrefix(comment.Text, "//go:linkname ") { + return true + } + } + return false +} + // eqlConsts reports whether two constant values, possibly nil, are equal. func eqlConsts(c1, c2 *ssa.Const) bool { if c1 == nil || c2 == nil { return c1 == c2 } - if c1.Type() != c2.Type() { + if !types.Identical(c1.Type(), c2.Type()) { return false } if c1.Value == nil || c2.Value == nil { @@ -190,6 +211,7 @@ func (c *Checker) Check() ([]Issue, error) { c.callByPos = make(map[token.Pos]*ast.CallExpr) c.funcBodyByPos = make(map[token.Pos]*ast.BlockStmt) c.typesImplementing = make(map[*types.Named][]string) + c.linknamed = make(map[token.Pos]bool) wantPkg := make(map[*types.Package]*packages.Package) genFiles := make(map[string]bool) @@ -221,6 +243,9 @@ func (c *Checker) Check() ([]Issue, error) { // FuncDecl.Name. case *ast.FuncDecl: c.funcBodyByPos[node.Name.Pos()] = node.Body + if linknameDoc(node.Doc) { + c.linknamed[node.Name.Pos()] = true + } case *ast.FuncLit: c.funcBodyByPos[node.Pos()] = node.Body } @@ -230,6 +255,30 @@ func (c *Checker) Check() ([]Issue, error) { } allFuncs := ssautil.AllFunctions(c.prog) + // ssautil.AllFunctions skips methods of unexported types unless they + // are reachable via an interface or an exported type, so we could miss + // call sites or entire functions to check. Add all the source-level + // funcs and methods from the loaded packages ourselves. + // We still want ssautil.AllFunctions for the synthetic wrappers it + // finds, such as those for promoted methods called via an interface, + // as their bodies record call sites too. + var addSrcFunc func(fn *ssa.Function) + addSrcFunc = func(fn *ssa.Function) { + allFuncs[fn] = true + for _, anon := range fn.AnonFuncs { + addSrcFunc(anon) + } + } + for _, pkg := range c.pkgs { + for _, def := range pkg.TypesInfo.Defs { + if def, ok := def.(*types.Func); ok { + if fn := c.prog.FuncValue(def); fn != nil { + addSrcFunc(fn) + } + } + } + } + // map from *ssa.FreeVar to *ssa.Function, to find function literals // behind closure vars in the simpler scenarios. freeVars := map[*ssa.FreeVar]*ssa.Function{} @@ -283,15 +332,14 @@ func (c *Checker) Check() ([]Issue, error) { // fn(someFunc()) fixes params c.paramsRequiredBy[fn] = "forwarded call" } - } - switch instr := instr.(type) { - case *ssa.Call: - for _, arg := range instr.Call.Args { + for _, arg := range instr.Common().Args { if fn := findFunction(freeVars, arg); fn != nil { - // someFunc(fn) + // someFunc(fn), also via go or defer c.signRequiredBy[fn] = "call" } } + } + switch instr := instr.(type) { case *ssa.Phi: for _, val := range instr.Edges { if fn := findFunction(freeVars, val); fn != nil { @@ -300,13 +348,14 @@ func (c *Checker) Check() ([]Issue, error) { } } case *ssa.Return: - for _, val := range instr.Results { + results := returnValues(instr) + for _, val := range results { if fn := findFunction(freeVars, val); fn != nil { // return fn c.signRequiredBy[fn] = "result" } } - if call := callExtract(instr, instr.Results); call != nil { + if call := callExtract(instr, results); call != nil { if fn := findFunction(freeVars, call.Call.Value); fn != nil { // return fn() c.resultsRequiredBy[fn] = "return" @@ -330,6 +379,26 @@ func (c *Checker) Check() ([]Issue, error) { if fn := findFunction(freeVars, instr.Val); fn != nil { c.signRequiredBy[fn] = as } + case *ssa.MapUpdate: + if fn := findFunction(freeVars, instr.Value); fn != nil { + // someMap[someKey] = fn + c.signRequiredBy[fn] = "map value" + } + case *ssa.Send: + if fn := findFunction(freeVars, instr.X); fn != nil { + // someChan <- fn + c.signRequiredBy[fn] = "channel send" + } + case *ssa.Select: + for _, state := range instr.States { + if state.Dir != types.SendOnly { + continue + } + if fn := findFunction(freeVars, state.Send); fn != nil { + // select { case someChan <- fn: } + c.signRequiredBy[fn] = "channel send" + } + } case *ssa.MakeInterface: // someIface(named) iface := instr.Type().Underlying().(*types.Interface) @@ -407,7 +476,9 @@ func findNamed(typ types.Type) *types.Named { case *types.Pointer: return findNamed(typ.Elem()) case *types.Named: - return typ + // Use the generic origin, so that an instantiation like G[int] + // and the receiver type of G's methods map to the same key. + return typ.Origin() } return nil } @@ -475,6 +546,10 @@ func (c *Checker) checkFunc(fn *ssa.Function, pkg *packages.Package) { c.debug(" skip - func signature required by %s\n", by) return } + if c.linknamed[fn.Pos()] { + c.debug(" skip - go:linkname directive\n") + return + } if recv := fn.Signature.Recv(); recv != nil { named := findNamed(recv.Type()) if slices.Contains(c.typesImplementing[named], fn.Name()) { @@ -503,6 +578,8 @@ func (c *Checker) checkFunc(fn *ssa.Function, pkg *packages.Package) { if !ok { continue } + // Note that we don't use returnValues here, as resolving loads + // could add reports rather than suppress them; see its doc. for i, val := range ret.Results { if _, ok := val.(*ssa.Extract); !ok { allRetsExtracting = false @@ -540,7 +617,7 @@ resLoop: continue } res := results.At(i) - if res.Type() == errorType { + if types.Unalias(res.Type()) == errorType { // "error is never used" is less useful, and it's up to // tools like errcheck anyway. continue @@ -610,7 +687,7 @@ resLoop: } func containsTypeParam(t types.Type) bool { - switch t := t.(type) { + switch t := types.Unalias(t).(type) { case *types.TypeParam, *types.Union: return true case *types.Struct: @@ -962,6 +1039,49 @@ func receivesExtractedArgs(freeVars map[*ssa.FreeVar]*ssa.Function, call ssa.Cal return nil } +// returnValues returns the values of a return instruction. In a function with +// deferred calls, go/ssa stores the results in local variables and loads them +// again after the deferred calls run; see through those stores and loads so +// that patterns like "return fn()" are still recognized. +// +// Note that a deferred call may modify the stored results, so the resolved +// values are only good enough to suppress reports, never to add them. +func returnValues(ret *ssa.Return) []ssa.Value { + var results []ssa.Value // lazily cloned from ret.Results + for i, val := range ret.Results { + if stored := storedValue(ret.Block(), val); stored != nil { + if results == nil { + results = slices.Clone(ret.Results) + } + results[i] = stored + } + } + if results == nil { + return ret.Results + } + return results +} + +// storedValue returns the value most recently stored within a block into the +// local variable that val loads, if any. +func storedValue(block *ssa.BasicBlock, val ssa.Value) ssa.Value { + load, ok := val.(*ssa.UnOp) + if !ok || load.Op != token.MUL { + return nil + } + alloc, ok := load.X.(*ssa.Alloc) + if !ok { + return nil + } + var stored ssa.Value + for _, instr := range block.Instrs { + if store, ok := instr.(*ssa.Store); ok && store.Addr == alloc { + stored = store.Val + } + } + return stored +} + // callExtract returns the call instruction fn(...) if it is used directly as // arguments to the parent instruction, such as fn2(fn(...)) or return fn(...). func callExtract(parent ssa.Instruction, values []ssa.Value) *ssa.Call {